[libcxx-commits] [clang] [libcxx] [clang & libcxx] constexpr pointer tagging (DO NOT MERGE) (PR #111861)
Hana Dusíková via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jan 13 09:27:01 PST 2025
https://github.com/hanickadot updated https://github.com/llvm/llvm-project/pull/111861
>From b8775689be276108ee1ed23304f9ee80cc95464a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Fri, 11 Oct 2024 13:57:25 +0200
Subject: [PATCH 1/9] tagged_ptr on older commit (so compilation works on my
mac)
---
clang/include/clang/AST/APValue.h | 4 +
clang/include/clang/Basic/Builtins.td | 37 ++
.../include/clang/Basic/DiagnosticASTKinds.td | 6 +
clang/lib/AST/APValue.cpp | 10 +
clang/lib/AST/ExprConstant.cpp | 112 ++++
clang/lib/CodeGen/CGBuiltin.cpp | 102 ++++
clang/lib/CodeGen/CodeGenFunction.h | 7 +
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__memory/pointer_traits.h | 9 +
libcxx/include/__memory/tagged_ptr.h | 516 ++++++++++++++++++
libcxx/include/memory | 4 +
.../memory/tagged_ptr/tagged_ptr.pass.cpp | 159 ++++++
.../tagged_ptr/tagged_ptr.pass.cpp.exec | Bin 0 -> 16856 bytes
13 files changed, 967 insertions(+)
create mode 100644 libcxx/include/__memory/tagged_ptr.h
create mode 100644 libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
create mode 100755 libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp.exec
diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h
index c4206b73b11562..300fa9cac5b12a 100644
--- a/clang/include/clang/AST/APValue.h
+++ b/clang/include/clang/AST/APValue.h
@@ -198,6 +198,8 @@ class APValue {
/// The QualType, if this is a DynamicAllocLValue.
void *DynamicAllocType;
};
+ public:
+ uint64_t Metadata{0};
};
/// A FieldDecl or CXXRecordDecl, along with a flag indicating whether we
@@ -483,6 +485,8 @@ class APValue {
}
const LValueBase getLValueBase() const;
+ uint64_t getLValueMetadata() const;
+ uint64_t & getLValueMetadata();
CharUnits &getLValueOffset();
const CharUnits &getLValueOffset() const {
return const_cast<APValue*>(this)->getLValueOffset();
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index b025a7681bfac3..ff427bca176323 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4774,3 +4774,40 @@ def ArithmeticFence : LangBuiltin<"ALL_LANGUAGES"> {
let Attributes = [CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}
+
+// support for pointer tagging
+// (ptr & mask) | (val & ~mask)
+def TagPointerMaskOr : Builtin {
+ let Spellings = ["__builtin_tag_pointer_mask_or"];
+ let Attributes = [Constexpr, NoThrow];
+ let Prototype = "void*(void*, size_t, size_t)";
+}
+
+// (ptr & mask) -> void *
+def TagPointerMask : Builtin {
+ let Spellings = ["__builtin_tag_pointer_mask"];
+ let Attributes = [Constexpr, NoThrow];
+ let Prototype = "void*(void*, size_t)";
+}
+
+// (ptr & mask) -> uintptr_t
+def TagPointerMaskAsInt : Builtin {
+ let Spellings = ["__builtin_tag_pointer_mask_as_int"];
+ let Attributes = [Constexpr, NoThrow];
+ let Prototype = "size_t(void*, size_t)";
+}
+
+// (ptr << shift) | (value & ~mask) -> void *
+// mask = (1 << shift) - 1
+def TagPointerShiftOr : Builtin {
+ let Spellings = ["__builtin_tag_pointer_shift_or"];
+ let Attributes = [Constexpr, NoThrow];
+ let Prototype = "void*(void*, size_t, size_t)";
+}
+
+// (ptr >> unshift) -> void *
+def TagPointerUnshift : Builtin {
+ let Spellings = ["__builtin_tag_pointer_unshift"];
+ let Attributes = [Constexpr, NoThrow];
+ let Prototype = "void*(void*, size_t)";
+}
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index a024f9b2a9f8c0..e31fe17bc22cb6 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -219,6 +219,12 @@ def note_constexpr_access_past_end : Note<
"destruction of}0 "
"dereferenced one-past-the-end pointer is not allowed "
"in a constant expression">;
+def note_constexpr_dereferencing_tagged_pointer: Note<
+ "dereferencing tagged pointer">;
+def note_constexpr_tagging_with_shift_zero: Note<
+ "you must shift pointer at least by one bit to store a tag">;
+def note_constexpr_tagging_with_empty_mask: Note<
+ "you must provide non-zero mask for pointer tagging">;
def note_constexpr_access_unsized_array : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
"member call on|dynamic_cast of|typeid applied to|construction of|"
diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp
index d8e33ff421c06c..acd3b8c9607b0e 100644
--- a/clang/lib/AST/APValue.cpp
+++ b/clang/lib/AST/APValue.cpp
@@ -976,6 +976,16 @@ const APValue::LValueBase APValue::getLValueBase() const {
return ((const LV *)(const void *)&Data)->Base;
}
+uint64_t APValue::getLValueMetadata() const {
+ assert(isLValue() && "Invalid accessor");
+ return ((const LV *)(const void *)&Data)->Base.Metadata;
+}
+
+uint64_t & APValue::getLValueMetadata() {
+ assert(isLValue() && "Invalid accessor");
+ return ((LV *)(void *)&Data)->Base.Metadata;
+}
+
bool APValue::isLValueOnePastTheEnd() const {
assert(isLValue() && "Invalid accessor");
return ((const LV *)(const void *)&Data)->IsOnePastTheEnd;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4d2d05307a6de9..26d6311ded2c65 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -4395,6 +4395,11 @@ handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type,
bool WantObjectRepresentation = false) {
if (LVal.Designator.Invalid)
return false;
+
+ if (LVal.Base.Metadata != 0) {
+ Info.FFDiag(Conv, diag::note_constexpr_dereferencing_tagged_pointer);
+ return false;
+ }
// Check for special cases where there is no existing APValue to look at.
const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
@@ -9627,6 +9632,87 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
return Success(E);
switch (BuiltinOp) {
+ // emulation of pointer tagging without actually touching pointer value
+ // as there is no such thing as address here, so tag is stored as a metadata in lvalue base
+ case Builtin::BI__builtin_tag_pointer_mask_or: {
+ APSInt Value, Mask;
+ if (!evaluatePointer(E->getArg(0), Result))
+ return Error(E);
+
+ if (!EvaluateInteger(E->getArg(1), Value, Info))
+ return Error(E);
+
+ if (!EvaluateInteger(E->getArg(2), Mask, Info))
+ return Error(E);
+
+ if (Mask.getLimitedValue() == 0) {
+ CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_empty_mask);
+ return false;
+ }
+
+ Result.Base.Metadata = (Result.Base.Metadata & ~Mask.getLimitedValue()) | (Value.getLimitedValue() & Mask.getLimitedValue());
+ return true;
+ }
+
+ // alternative approach to tagging which shifts pointer
+ // here we are only shifting metadata
+ case Builtin::BI__builtin_tag_pointer_shift_or: {
+ APSInt Value, Shift;
+ if (!evaluatePointer(E->getArg(0), Result))
+ return Error(E);
+
+ if (!EvaluateInteger(E->getArg(1), Value, Info))
+ return Error(E);
+
+ if (!EvaluateInteger(E->getArg(2), Shift, Info))
+ return Error(E);
+
+ if (Shift.getLimitedValue() == 0) {
+ CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_shift_zero);
+ return false;
+ }
+
+ const uint64_t Mask = (1ull << static_cast<uint64_t>(Shift.getLimitedValue())) - 1ull;
+ Result.Base.Metadata = (Result.Base.Metadata << static_cast<uint64_t>(Shift.getLimitedValue())) | (Value.getLimitedValue() & Mask);
+ return true;
+ }
+
+ // recover pointer by masking metadata
+ // exprconstant allows dereferencing only metadata == 0 pointer
+ case Builtin::BI__builtin_tag_pointer_mask: {
+ APSInt Mask;
+ if (!evaluatePointer(E->getArg(0), Result))
+ return Error(E);
+
+ if (!EvaluateInteger(E->getArg(1), Mask, Info))
+ return Error(E);
+
+ if (Mask.getLimitedValue() == 0) {
+ CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_empty_mask);
+ return false;
+ }
+
+ Result.Base.Metadata = (Result.Base.Metadata & Mask.getLimitedValue());
+ return true;
+ }
+
+ // shifting back pointer (also can convert tagged pointer back to normal pointer)
+ case Builtin::BI__builtin_tag_pointer_unshift: {
+ APSInt Shift;
+ if (!evaluatePointer(E->getArg(0), Result))
+ return Error(E);
+
+ if (!EvaluateInteger(E->getArg(1), Shift, Info))
+ return Error(E);
+
+ if (Shift.getLimitedValue() == 0) {
+ CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_shift_zero);
+ return false;
+ }
+
+ Result.Base.Metadata = (Result.Base.Metadata >> static_cast<uint64_t>(Shift.getLimitedValue()));
+ return true;
+ }
case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
@@ -12520,6 +12606,25 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
default:
return false;
+ case Builtin::BI__builtin_tag_pointer_mask_as_int: {
+ LValue Pointer;
+ APSInt Mask;
+
+ if (!EvaluatePointer(E->getArg(0), Pointer, Info))
+ return Error(E);
+
+ if (!EvaluateInteger(E->getArg(1), Mask, Info))
+ return Error(E);
+
+ if (Mask.getLimitedValue() == 0) {
+ CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_empty_mask);
+ return false;
+ }
+
+ const uint64_t Result = Pointer.Base.Metadata & (static_cast<uint64_t>(Mask.getLimitedValue()));
+ return Success(Result, E);
+ }
+
case Builtin::BI__builtin_dynamic_object_size:
case Builtin::BI__builtin_object_size: {
// The type was checked when we built the expression.
@@ -13904,6 +14009,13 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
return Success(CmpResult::Less, E);
if (CompareLHS > CompareRHS)
return Success(CmpResult::Greater, E);
+
+ // this makes tagged pointer not equal to original pointer
+ if (LHSValue.Base.Metadata < RHSValue.Base.Metadata)
+ return Success(CmpResult::Less, E);
+ if (LHSValue.Base.Metadata > RHSValue.Base.Metadata)
+ return Success(CmpResult::Greater, E);
+
return Success(CmpResult::Equal, E);
}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index d1af7fde157b64..7df0077dee5a80 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -5254,6 +5254,19 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(Carry);
}
+
+ // support for pointer tagging
+ case Builtin::BI__builtin_tag_pointer_mask_or:
+ return EmitBuiltinTagPointerMaskOr(E);
+ case Builtin::BI__builtin_tag_pointer_mask:
+ return EmitBuiltinTagPointerMask(E);
+ case Builtin::BI__builtin_tag_pointer_mask_as_int:
+ return EmitBuiltinTagPointerMaskAsInt(E);
+ case Builtin::BI__builtin_tag_pointer_shift_or:
+ return EmitBuiltinTagPointerShiftOr(E);
+ case Builtin::BI__builtin_tag_pointer_unshift:
+ return EmitBuiltinTagPointerUnshift(E);
+
case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
@@ -21030,6 +21043,95 @@ Value *CodeGenFunction::EmitNVPTXBuiltinExpr(unsigned BuiltinID,
}
}
+/// Generate (x & ~mask) | (value & mask).
+RValue CodeGenFunction::EmitBuiltinTagPointerMaskOr(const CallExpr *E) {
+ llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
+ llvm::Value * Value = EmitScalarExpr(E->getArg(1));
+ llvm::Value * Mask = EmitScalarExpr(E->getArg(2));
+
+ llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));
+
+ // TODO: avoid using bitcast and go path of ptr.tag (mirror to ptr.mask)
+ // to keep pointer's provenance, but this turns out a bit harder to do as it touches
+ // a lot of places in llvm
+ llvm::Value * PointerInt = Builder.CreateBitOrPointerCast(Ptr, IntType, "pointer_int");
+ llvm::Value * InvertedMask = Builder.CreateNot(Mask, "inverted_mask");
+
+ llvm::Value * MaskedPtr = Builder.CreateAnd(PointerInt, InvertedMask, "masked_ptr");
+ llvm::Value * MaskedValue = Builder.CreateAnd(Value, Mask, "masked_value");
+
+ llvm::Value * ResultInt = Builder.CreateOr(MaskedPtr, MaskedValue, "result_int");
+ llvm::Value * Result = Builder.CreateBitOrPointerCast(ResultInt, Ptr->getType(), "result_ptr");
+
+ return RValue::get(Result);
+}
+
+/// Generate (x << shift) | (value & ((1 << shift) - 1)).
+RValue CodeGenFunction::EmitBuiltinTagPointerShiftOr(const CallExpr *E) {
+ llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
+ llvm::Value * Value = EmitScalarExpr(E->getArg(1));
+ llvm::Value * Shift = EmitScalarExpr(E->getArg(2));
+
+ llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));
+
+ // TODO: again, for now a bitcast, later ptr.shift_tag
+ llvm::Value * PointerInt = Builder.CreateBitOrPointerCast(Ptr, IntType, "pointer_int");
+ llvm::Value * ShiftedPointerInt = Builder.CreateShl(PointerInt, Shift);
+
+ auto *One = llvm::ConstantInt::get(IntType, 1);
+
+ llvm::Value * Mask = Builder.CreateSub(Builder.CreateShl(One, Shift), One, "mask");
+ llvm::Value * MaskedValue = Builder.CreateAdd(Value, Mask, "masked_value");
+ llvm::Value * PointerWithTag = Builder.CreateOr(ShiftedPointerInt, MaskedValue, "pointer_with_tag_int");
+
+ llvm::Value * Result = Builder.CreateBitOrPointerCast(PointerWithTag, Ptr->getType(), "result_ptr");
+ return RValue::get(Result);
+}
+
+/// Generate (x >> shift)
+RValue CodeGenFunction::EmitBuiltinTagPointerUnshift(const CallExpr *E) {
+ llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
+ llvm::Value * Shift = EmitScalarExpr(E->getArg(1));
+
+ llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));
+
+ // for now I'm going path of bitcast
+ llvm::Value * PointerInt = Builder.CreateBitOrPointerCast(Ptr, IntType, "pointer_int");
+ llvm::Value * UnShiftedPointerInt = Builder.CreateAShr(PointerInt, Shift, "unshifted_pointer_int");
+
+ llvm::Value * Result = Builder.CreateBitOrPointerCast(UnShiftedPointerInt, Ptr->getType(), "result_ptr");
+ return RValue::get(Result);
+}
+
+/// Generate (x & mask).
+RValue CodeGenFunction::EmitBuiltinTagPointerMask(const CallExpr *E) {
+ llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
+ llvm::Value * Mask = EmitScalarExpr(E->getArg(1));
+
+ llvm::Value *Result = Builder.CreateIntrinsic(
+ Intrinsic::ptrmask, {Ptr->getType(), Mask->getType()},
+ {Ptr, Mask}, nullptr, "result");
+
+ return RValue::get(Result);
+}
+
+/// Generate (x & mask) (but return it as number).
+RValue CodeGenFunction::EmitBuiltinTagPointerMaskAsInt(const CallExpr *E) {
+ llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
+ llvm::Value * Mask = EmitScalarExpr(E->getArg(1));
+
+ llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));
+
+ llvm::Value *Result = Builder.CreateIntrinsic(
+ Intrinsic::ptrmask, {Ptr->getType(), Mask->getType()},
+ {Ptr, Mask}, nullptr, "result");
+
+ llvm::Value * IntResult = Builder.CreateBitOrPointerCast(Result, IntType, "int_result");
+
+ return RValue::get(IntResult);
+}
+
+
namespace {
struct BuiltinAlignArgs {
llvm::Value *Src = nullptr;
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 1c0a0e117e5607..4fc0d5f9ff1fed 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4546,6 +4546,13 @@ class CodeGenFunction : public CodeGenTypeCache {
RValue emitRotate(const CallExpr *E, bool IsRotateRight);
+ /// Emit IR for pointer tagging
+ RValue EmitBuiltinTagPointerMaskOr(const CallExpr *E);
+ RValue EmitBuiltinTagPointerMask(const CallExpr *E);
+ RValue EmitBuiltinTagPointerMaskAsInt(const CallExpr *E);
+ RValue EmitBuiltinTagPointerShiftOr(const CallExpr *E);
+ RValue EmitBuiltinTagPointerUnshift(const CallExpr *E);
+
/// Emit IR for __builtin_os_log_format.
RValue emitBuiltinOSLogFormat(const CallExpr &E);
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8e..7be458708c8101 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -543,6 +543,7 @@ set(files
__memory/raw_storage_iterator.h
__memory/shared_ptr.h
__memory/swap_allocator.h
+ __memory/tagged_ptr.h
__memory/temp_value.h
__memory/temporary_buffer.h
__memory/uninitialized_algorithms.h
diff --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h
index 0914aceb318b74..983bbe25680866 100644
--- a/libcxx/include/__memory/pointer_traits.h
+++ b/libcxx/include/__memory/pointer_traits.h
@@ -162,6 +162,15 @@ struct _LIBCPP_TEMPLATE_VIS pointer_traits<_Tp*> {
};
#endif
+#if _LIBCPP_STD_VER >= 26
+ template <unsigned OverAlignment = alignof(element_type)>
+ static constexpr uintptr_t available_bits = ((1ull << OverAlignment) - 1ull);
+
+ // what to do with upper bits we can't detect at compile-time?
+ template <unsigned OverAlignment = alignof(element_type)>
+ static constexpr uintptr_t significant_bits = ~available_bits<OverAlignment>;
+#endif
+
private:
struct __nat {};
diff --git a/libcxx/include/__memory/tagged_ptr.h b/libcxx/include/__memory/tagged_ptr.h
new file mode 100644
index 00000000000000..b0eafc633222a8
--- /dev/null
+++ b/libcxx/include/__memory/tagged_ptr.h
@@ -0,0 +1,516 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___TAGGED_PTR_H
+#define _LIBCPP___TAGGED_PTR_H
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+
+#include <__config>
+#include <__type_traits/is_trivially_copyable.h>
+#include <__assert>
+#include "__bit/has_single_bit.h"
+#include <__type_traits/rank.h>
+#include "pointer_traits.h"
+#include <compare>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <typename T, typename Y> concept convertible_to_from = std::convertible_to<Y, T> && std::convertible_to<T, Y>;
+
+template <typename T> concept pointer_tagging_schema = requires(T::dirty_pointer payload, T::clean_pointer clean, T::tag_type tag) {
+ requires convertible_to_from<typename T::tag_type, uintptr_t>;
+ requires std::is_pointer_v<typename T::clean_pointer>;
+
+ { T::encode_pointer_with_tag(clean, tag) } noexcept -> std::same_as<typename T::dirty_pointer>;
+ { T::recover_pointer(payload) } noexcept -> std::same_as<typename T::clean_pointer>;
+ { T::recover_value(payload) } noexcept -> std::same_as<typename T::tag_type>;
+};
+
+template <typename T> concept pointer_tagging_schema_with_aliasing = pointer_tagging_schema<T> && requires(T::dirty_pointer payload) {
+ { T::recover_aliasing_pointer(payload) } noexcept -> std::same_as<typename T::clean_pointer>;
+};
+
+// no-op schema so I can better explain how schemas work
+struct no_tag {
+ template <typename T, typename Tag> struct schema {
+ using clean_pointer = T *;
+ using dirty_pointer = void *;
+ using tag_type = Tag;
+
+ [[clang::always_inline]] static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type) noexcept {
+ return (dirty_pointer)_ptr;
+ }
+ [[clang::always_inline]] static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
+ return (clean_pointer)_ptr;
+ }
+ [[clang::always_inline]] static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
+ return (clean_pointer)_ptr;
+ }
+ [[clang::always_inline]] static constexpr tag_type recover_value(dirty_pointer) noexcept {
+ return {};
+ }
+ };
+};
+
+// most basic schema for tagging
+// it lets user to provide their own mask
+template <uintptr_t Mask> struct bitmask_tag {
+ static constexpr uintptr_t _mask = Mask;
+
+ template <typename T, typename Tag> struct schema {
+ using clean_pointer = T *;
+ using dirty_pointer = void *;
+ using tag_type = Tag;
+
+ [[clang::always_inline]] static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+#if __has_builtin(__builtin_tag_pointer_mask_or)
+ return static_cast<dirty_pointer>(__builtin_tag_pointer_mask_or((void *)(_ptr), static_cast<uintptr_t>(_value), _mask));
+#else
+ return reinterpret_cast<dirty_pointer>((reinterpret_cast<uintptr_t>(_ptr) & static_cast<uintptr_t>(_mask)) | (static_cast<uintptr_t>(_value) & ~static_cast<uintptr_t>(_mask)));
+#endif
+ }
+ [[clang::always_inline]] static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
+#if __has_builtin(__builtin_tag_pointer_mask)
+ return static_cast<clean_pointer>(__builtin_tag_pointer_mask((void *)_ptr, ~_mask));
+#else
+ return reinterpret_cast<clean_pointer>(reinterpret_cast<uintptr_t>(_ptr) & ~static_cast<uintptr_t>(_mask));
+#endif
+ }
+ [[clang::always_inline]] static constexpr tag_type recover_value(dirty_pointer _ptr) noexcept {
+#if __has_builtin(__builtin_tag_pointer_mask_as_int)
+ return static_cast<tag_type>(__builtin_tag_pointer_mask_as_int((void *)_ptr, _mask));
+#else
+ return static_cast<tag_type>(reinterpret_cast<uintptr_t>(_ptr) & static_cast<uintptr_t>(_mask));
+#endif
+ }
+ };
+};
+
+// schema which allows only pointer of custom provided minimal alignment
+// otherwise it behaves as custom mask schema
+template <unsigned Alignment> struct custom_alignment_tag {
+ static constexpr uintptr_t mask = (static_cast<uintptr_t>(1u) << static_cast<uintptr_t>(Alignment)) - 1ull;
+ template <typename T, typename Tag> struct schema: bitmask_tag<mask>::template schema<T, Tag> {
+ using _underlying_schema =bitmask_tag<mask>::template schema<T, Tag>;
+
+ using clean_pointer = _underlying_schema::clean_pointer;
+ using dirty_pointer = _underlying_schema::dirty_pointer;
+ using tag_type = _underlying_schema::tag_type;
+
+ [[clang::always_inline]] static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+#if __has_builtin(__builtin_is_aligned)
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__builtin_is_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignemt for tagging");
+#else
+ if !consteval {
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(reinterpret_cast<uintptr_t>(std::addressof(_ptr)) % Alignment == 0, "Pointer must be aligned by provided alignemt for tagging");
+ }
+#endif
+ return _underlying_schema::encode_pointer_with_tag(_ptr, _value);
+ }
+
+ using _underlying_schema::recover_pointer;
+ using _underlying_schema::recover_value;
+ };
+};
+
+// default scheme which gives only bits from alignment
+struct alignment_low_bits_tag {
+ template <typename T> static constexpr unsigned alignment = alignof(T);
+ template <typename T, typename Tag> using schema = typename custom_alignment_tag<alignment<T>>::template schema<T, Tag>;
+};
+
+// scheme which shifts bits to left by Bits bits and gives the space for tagging
+template <unsigned Bits> struct shift_tag {
+ static constexpr unsigned _shift = Bits;
+ static constexpr uintptr_t _mask = (uintptr_t{1u} << _shift) - 1u;
+
+ template <typename T, typename Tag> struct schema {
+ using clean_pointer = T *;
+ using dirty_pointer = void *;
+ using tag_type = Tag;
+
+ [[clang::always_inline]] static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+#if __has_builtin(__builtin_tag_pointer_shift_or)
+ return static_cast<dirty_pointer>(__builtin_tag_pointer_shift_or((void *)(_ptr), (uintptr_t)_value, _shift));
+#else
+ return reinterpret_cast<dirty_pointer>((reinterpret_cast<uintptr_t>(_ptr) << _shift) | (static_cast<uintptr_t>(_value) & ((1ull << static_cast<uintptr_t>(_shift)) - 1ull)));
+#endif
+ }
+ [[clang::always_inline]] static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
+#if __has_builtin(__builtin_tag_pointer_unshift)
+ return static_cast<clean_pointer>(__builtin_tag_pointer_unshift((void *)_ptr, _shift));
+#else
+ return reinterpret_cast<clean_pointer>(reinterpret_cast<uintptr_t>(_ptr) >> _shift);
+#endif
+ }
+ [[clang::always_inline]] static constexpr tag_type recover_value(dirty_pointer _ptr) noexcept {
+#if __has_builtin(__builtin_tag_pointer_mask_as_int)
+ return static_cast<tag_type>(__builtin_tag_pointer_mask_as_int((void *)_ptr, _mask));
+#else
+ return static_cast<tag_type>(reinterpret_cast<uintptr_t>(_ptr) & static_cast<uintptr_t>(_mask));
+#endif
+ }
+ };
+};
+
+// scheme which shifts pointer to left by 8 bits and give this space as guaranteed space for tagging
+struct low_byte_tag {
+ template <typename T, typename Tag> using schema = typename shift_tag<8>::template schema<T, Tag>;
+};
+
+// this will give user access to upper byte of pointer on aarch64
+// also it supports recovering aliasing pointer as no-op (fast-path)
+struct upper_byte_tag {
+ template <typename T> static constexpr unsigned _shift = sizeof(T *) * 8ull - 8ull;
+ template <typename T> static constexpr uintptr_t _mask = 0b1111'1111ull << _shift<T>;
+
+ template <typename T, typename Tag> struct schema: bitmask_tag<_mask<T>>::template schema<T, Tag> {
+ using _underlying_schema = bitmask_tag<_mask<T>>::template schema<T, Tag>;
+
+ using clean_pointer = _underlying_schema::clean_pointer;
+ using dirty_pointer = _underlying_schema::dirty_pointer;
+ using tag_type = _underlying_schema::tag_type;
+
+ [[clang::always_inline]] static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
+ return (clean_pointer)_ptr;
+ }
+
+ using _underlying_schema::encode_pointer_with_tag;
+ using _underlying_schema::recover_pointer;
+ using _underlying_schema::recover_value;
+ };
+};
+
+// improved version of previous aarch64 upper byte scheme
+// with added shifting tag value into position, so the tag doesn't need to know about exact position
+struct upper_byte_shifted_tag: upper_byte_tag {
+ template <typename T, typename Tag> struct schema: upper_byte_tag::template schema<T, uintptr_t> {
+ using _underlying_schema = upper_byte_tag::template schema<T, uintptr_t>;
+
+ using clean_pointer = _underlying_schema::clean_pointer;
+ using dirty_pointer = _underlying_schema::dirty_pointer;
+ using tag_type = Tag;
+
+ [[clang::always_inline]] static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+ return _underlying_schema::encode_pointer_with_tag(_ptr, static_cast<uintptr_t>(_value) << upper_byte_tag::_shift<T>);
+ }
+ [[clang::always_inline]] static constexpr tag_type recover_value(dirty_pointer _ptr) noexcept {
+ return static_cast<tag_type>(_underlying_schema::recover_value(_ptr) >> upper_byte_tag::_shift<T>);
+ }
+
+ using _underlying_schema::recover_pointer;
+ using _underlying_schema::recover_aliasing_pointer;
+ };
+};
+
+// forward declaration
+template <typename _T, typename _Tag = uintptr_t, typename _Schema = alignment_low_bits_tag> class tagged_ptr;
+
+
+template <typename _Schema, typename _T, typename _Tag = uintptr_t> constexpr auto tag_ptr(_T * _ptr, _Tag _tag = {}) noexcept {
+ return tagged_ptr<_T, _Tag, _Schema>{_ptr, _tag};
+}
+
+template <typename _T, typename _Tag, typename _Schema = alignment_low_bits_tag> constexpr auto tagged_pointer_cast(typename _Schema::template schema<_T, _Tag>::dirty_pointer _ptr) noexcept -> tagged_ptr<_T, _Tag, _Schema> {
+ using result_type = tagged_ptr<_T, _Tag, _Schema>;
+ return result_type{typename result_type::already_tagged_tag{_ptr}};
+}
+
+template <typename _Schema2, typename _T, typename _Tag, typename _Schema> constexpr auto scheme_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
+ return tagged_ptr<_T, _Tag, _Schema2>{in.pointer(), in.tag()};
+}
+
+template <typename _Y, typename _T, typename _Tag, typename _Schema> constexpr auto const_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
+ // TODO we can just use native pointer here
+ return tagged_ptr<_Y, _Tag, _Schema>{const_cast<_Y*>(in.pointer()), in.tag()};
+}
+
+template <typename _Y, typename _T, typename _Tag, typename _Schema> constexpr auto static_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
+ return tagged_ptr<_Y, _Tag, _Schema>{static_cast<_Y*>(in.pointer()), in.tag()};
+}
+
+template <typename _Y, typename _T, typename _Tag, typename _Schema> constexpr auto dynamic_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
+ return tagged_ptr<_Y, _Tag, _Schema>{dynamic_cast<_Y*>(in.pointer()), in.tag()};
+}
+
+template <typename _Y, typename _T, typename _Tag, typename _Schema> auto reinterpret_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
+ return tagged_ptr<_Y, _Tag, _Schema>{reinterpret_cast<_Y*>(in.pointer()), in.tag()};
+}
+
+
+// wrapper class containing the pointer value and provides access
+template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
+public:
+ using schema = typename _Schema::template schema<_T, _Tag>;
+ using dirty_pointer = typename schema::dirty_pointer;
+ using clean_pointer = typename schema::clean_pointer;
+ using tag_type = typename schema::tag_type;
+
+ using value_type = std::remove_cvref_t<decltype(*std::declval<clean_pointer>())>;
+ using difference_type = typename std::pointer_traits<clean_pointer>::difference_type;
+
+
+ template <typename _Y> using rebind = tagged_ptr<_Y, _Tag, _Schema>;
+
+private:
+
+ dirty_pointer _pointer{nullptr};
+
+ friend constexpr auto tagged_pointer_cast<_T, _Tag, _Schema>(typename _Schema::template schema<_T, _Tag>::dirty_pointer ptr) noexcept -> tagged_ptr<_T, _Tag, _Schema>;
+
+ struct already_tagged_tag {
+ dirty_pointer _ptr;
+ };
+
+ // special hidden constructor to allow constructing unsafely
+ [[clang::always_inline]] constexpr tagged_ptr(already_tagged_tag _in) noexcept: _pointer{_in._ptr} { }
+
+ template <typename _Y, typename _T2, typename _Tag2, typename _Schema2> constexpr auto const_pointer_cast(tagged_ptr<_T2, _Tag2, _Schema2> in) noexcept -> rebind<_T>;
+
+public:
+ tagged_ptr() = default;
+ consteval tagged_ptr(nullptr_t) noexcept: _pointer{nullptr} { }
+ tagged_ptr(const tagged_ptr &) = default;
+ tagged_ptr(tagged_ptr &&) = default;
+ ~tagged_ptr() = default;
+ tagged_ptr & operator=(const tagged_ptr &) = default;
+ tagged_ptr & operator=(tagged_ptr &&) = default;
+
+ [[clang::always_inline]] explicit constexpr tagged_ptr(clean_pointer _ptr, tag_type _tag = {}) noexcept: _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
+ _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
+ _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
+ }
+
+ // accessors
+ [[clang::always_inline]] constexpr decltype(auto) operator*() const noexcept {
+ return *pointer();
+ }
+
+ [[clang::always_inline]] constexpr clean_pointer operator->() const noexcept {
+ return pointer();
+ }
+
+ template <typename...Ts> [[clang::always_inline]] [[clang::always_inline]] constexpr decltype(auto) operator[](Ts... args) const noexcept requires std::is_array_v<value_type> && (sizeof...(Ts) == std::rank_v<value_type>) {
+ return (*pointer())[args...];
+ }
+
+ [[clang::always_inline]] constexpr decltype(auto) operator[](difference_type diff) const noexcept requires (!std::is_array_v<value_type>) {
+ return *(pointer() + diff);
+ }
+
+ // swap
+ [[clang::always_inline]] friend constexpr void swap(tagged_ptr & lhs, tagged_ptr & rhs) noexcept {
+ std::swap(lhs._pointer, rhs._pointer);
+ }
+
+ // modifiers for tag
+ [[clang::always_inline]] constexpr auto & set(tag_type new_tag) noexcept {
+ // this is here so I can avoid checks
+ // TODO we should be able to check what bits available
+ _pointer = schema::encode_pointer_with_tag(pointer(), new_tag);
+ return *this;
+ }
+
+ [[clang::always_inline]] constexpr auto & set_union(tag_type addition) noexcept {
+ return set(tag() | addition);
+ }
+
+ [[clang::always_inline]] constexpr auto & set_difference(tag_type mask) noexcept {
+ return set(tag() & (~static_cast<uintptr_t>(mask)));
+ }
+
+ [[clang::always_inline]] constexpr auto & set_intersection(tag_type mask) noexcept {
+ return set(tag() & mask);
+ }
+
+ [[clang::always_inline]] constexpr auto & set_all() noexcept {
+ return set(static_cast<tag_type>(0xFFFFFFFF'FFFFFFFFull));
+ }
+
+ // modifiers for pointer
+ [[clang::always_inline]] constexpr auto & operator++() noexcept {
+ _pointer = tagged_ptr{pointer()+1u, tag()}._pointer;
+ return *this;
+ }
+
+ [[clang::always_inline]] constexpr auto operator++(int) noexcept {
+ auto copy = auto(*this);
+ this->operator++();
+ return copy;
+ }
+
+ [[clang::always_inline]] constexpr auto & operator+=(difference_type diff) noexcept {
+ _pointer = tagged_ptr{pointer()+diff, tag()}._pointer;
+ return *this;
+ }
+
+ [[clang::always_inline]] friend constexpr auto operator+(tagged_ptr lhs, difference_type diff) noexcept {
+ lhs += diff;
+ return lhs;
+ }
+
+ [[clang::always_inline]] friend constexpr auto operator+(difference_type diff, tagged_ptr rhs) noexcept {
+ rhs += diff;
+ return rhs;
+ }
+
+ [[clang::always_inline]] friend constexpr auto operator-(tagged_ptr lhs, difference_type diff) noexcept {
+ lhs -= diff;
+ return lhs;
+ }
+
+ [[clang::always_inline]] friend constexpr auto operator-(difference_type diff, tagged_ptr rhs) noexcept {
+ rhs -= diff;
+ return rhs;
+ }
+
+ [[clang::always_inline]] constexpr auto & operator-=(difference_type diff) noexcept {
+ _pointer = tagged_ptr{pointer()-diff, tag()}._pointer;
+ return *this;
+ }
+
+ [[clang::always_inline]] constexpr auto & operator--() noexcept {
+ _pointer = tagged_ptr{pointer()-1u, tag()}._pointer;
+ return *this;
+ }
+
+ [[clang::always_inline]] constexpr auto operator--(int) noexcept {
+ auto copy = auto(*this);
+ this->operator--();
+ return copy;
+ }
+
+ // observers
+ constexpr dirty_pointer unsafe_dirty_pointer() const noexcept {
+ // this function is not intentionally constexpr, as it is needed only to interact with
+ // existing runtime code
+ return _pointer;
+ }
+
+ static constexpr bool support_aliasing_masking = pointer_tagging_schema_with_aliasing<schema>;
+
+ [[clang::always_inline]] constexpr clean_pointer aliasing_pointer() const noexcept {
+ if constexpr (support_aliasing_masking) {
+ if !consteval {
+ return schema::recover_aliasing_pointer(_pointer);
+ }
+ }
+
+ return schema::recover_pointer(_pointer);
+ }
+
+ [[clang::always_inline]] constexpr clean_pointer pointer() const noexcept {
+ return schema::recover_pointer(_pointer);
+ }
+
+ [[clang::always_inline]] constexpr tag_type tag() const noexcept {
+ return schema::recover_value(_pointer);
+ }
+
+ template <std::size_t I> [[nodiscard, clang::always_inline]] friend constexpr decltype(auto) get(tagged_ptr _pair) noexcept {
+ static_assert(I < 3);
+ if constexpr (I == 0) {
+ return _pair.pointer();
+ } else {
+ return _pair.tag();
+ }
+ }
+
+ [[clang::always_inline]] constexpr explicit operator bool() const noexcept {
+ return pointer() != nullptr;
+ }
+
+ [[clang::always_inline]] friend constexpr ptrdiff_t operator-(tagged_ptr lhs, tagged_ptr rhs) noexcept {
+ return lhs.pointer() - rhs.pointer();
+ }
+
+ // comparison operators
+ [[clang::always_inline]] friend bool operator==(tagged_ptr, tagged_ptr) = default;
+
+ struct _compare_object {
+ clean_pointer pointer;
+ tag_type tag;
+
+ friend auto operator<=>(_compare_object, _compare_object) = default;
+ };
+
+ [[clang::always_inline]] friend constexpr auto operator<=>(tagged_ptr lhs, tagged_ptr rhs) noexcept {
+ return _compare_object{lhs.pointer(), lhs.tag()} <=> _compare_object{rhs.pointer(), rhs.tag()};
+ }
+ [[clang::always_inline]] friend constexpr bool operator==(tagged_ptr lhs, clean_pointer rhs) noexcept {
+ return lhs.pointer() == rhs;
+ }
+ [[clang::always_inline]] friend constexpr auto operator<=>(tagged_ptr lhs, clean_pointer rhs) noexcept {
+ return lhs.pointer() <=> rhs;
+ }
+ [[clang::always_inline]] friend constexpr bool operator==(tagged_ptr lhs, nullptr_t) noexcept {
+ return lhs.pointer() == nullptr;
+ }
+};
+
+// to_address specialization
+template <typename _T, typename _Tag, typename _Schema> static constexpr auto to_address(tagged_ptr<_T, _Tag, _Schema> p) noexcept -> tagged_ptr<_T, _Tag, _Schema>::element_type * {
+ return p.pointer();
+}
+
+// iterator traits
+template <typename _T, typename _Tag, typename _Schema>
+struct _LIBCPP_TEMPLATE_VIS iterator_traits<tagged_ptr<_T, _Tag, _Schema>> {
+ using _tagged_ptr = tagged_ptr<_T, _Tag, _Schema>;
+
+ using iterator_category = std::random_access_iterator_tag;
+ using iterator_concept = std::contiguous_iterator_tag;
+
+ using value_type = _tagged_ptr::value_type;
+ using reference = value_type &;
+ using pointer = _tagged_ptr::clean_pointer;
+ using difference_type = _tagged_ptr::difference_type;
+};
+
+// pointer traits
+template <typename _T, typename _Tag, typename _Schema>
+struct _LIBCPP_TEMPLATE_VIS pointer_traits<tagged_ptr<_T, _Tag, _Schema>> {
+ using _tagged_ptr = tagged_ptr<_T, _Tag, _Schema>;
+ using pointer = _tagged_ptr::clean_pointer;
+ using element_type = _tagged_ptr::value_type;
+ using difference_type = _tagged_ptr::difference_type;
+
+ template <typename _Up> using rebind = typename _tagged_ptr::template rebind<_Up>;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI constexpr static _tagged_ptr pointer_to(pointer ptr) _NOEXCEPT {
+ return _tagged_ptr{ptr};
+ }
+};
+
+// we are defaulting always to low_bits schema
+template <typename _T> tagged_ptr(_T *) -> tagged_ptr<_T>;
+template <typename _T, typename _Tag> tagged_ptr(_T *, _Tag) -> tagged_ptr<_T, _Tag>;
+
+// support for tuple protocol so we can split tagged pointer to structured bindings:
+// auto [ptr, tag] = tagged_ptr
+template <typename _T, typename _Tag, typename _Schema>
+struct tuple_size<tagged_ptr<_T, _Tag, _Schema>>: std::integral_constant<std::size_t, 2> {};
+
+template <std::size_t I, typename _T, typename _Tag, typename _Schema>
+struct tuple_element<I, tagged_ptr<_T, _Tag, _Schema>> {
+ using _pair_type = tagged_ptr<_T, _Tag, _Schema>;
+ using type = std::conditional_t<I == 0, typename _pair_type::clean_pointer, typename _pair_type::tag_type>;
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP___TAGGED_PTR_H
diff --git a/libcxx/include/memory b/libcxx/include/memory
index b940a32c3ebe6c..da48b1b4661cb8 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -969,6 +969,10 @@ template<class Pointer = void, class Smart, class... Args>
# include <__memory/allocate_at_least.h>
#endif
+#if _LIBCPP_STD_VER >= 26
+# include <__memory/tagged_ptr.h>
+#endif
+
#include <version>
// [memory.syn]
diff --git a/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp b/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
new file mode 100644
index 00000000000000..4b935ed69905fd
--- /dev/null
+++ b/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
@@ -0,0 +1,159 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+
+// <memory>
+
+#include <cassert>
+#include <memory>
+
+//#include "test_macros.h"
+
+template <bool Expected> struct test_helper {
+ template <typename Lmbd> auto & operator=(Lmbd && lmbd) {
+ if constexpr (Expected) {
+ static_assert(Lmbd{}());
+ assert(lmbd());
+ } else {
+ static_assert(!Lmbd{}());
+ assert(!lmbd());
+ }
+
+ return *this;
+ }
+};
+
+#define HANA_TEST test_helper<true>{} = []
+#define HANA_FAIL_TEST test_helper<false>{} = []
+#define HANA_ASSERT(expr) do { if (!static_cast<bool>(expr)) { if consteval { throw false; } return false; }} while (false)
+
+template <typename T> struct tagged_range {
+ using scheme = std::no_tag;
+
+ const T * start;
+ size_t size;
+
+ constexpr tagged_range(const T * in, std::size_t sz) noexcept: start{in}, size{sz} { }
+
+ constexpr auto begin() const {
+ return std::tag_ptr<scheme>(start);
+ }
+
+ constexpr auto end() const {
+ return std::tag_ptr<scheme>(start+size);
+ }
+};
+
+#if TEST_STD_VER >= 26
+int main(int, char**) {
+
+HANA_TEST {
+ int64_t a = 42;
+
+ uintptr_t tag = 0b101u;
+ auto tptr = std::tagged_ptr(&a, tag);
+
+ HANA_ASSERT(tptr.unsafe_dirty_pointer() != &a);
+
+ int64_t * original = tptr.pointer();
+ HANA_ASSERT(tag == tptr.tag());
+ HANA_ASSERT(original == &a);
+
+ auto [p, t] = tptr;
+ HANA_ASSERT(p == &a);
+ HANA_ASSERT(t == tag);
+ return true;
+};
+
+HANA_TEST {
+ int64_t a[3] = {1,2,3};
+
+ uintptr_t tag = 0b11000111u;
+ auto tptr = std::tag_ptr<std::low_byte_tag>(&a, tag);
+
+ auto * original = tptr.pointer();
+ HANA_ASSERT(tag == tptr.tag());
+ HANA_ASSERT(original == &a);
+
+ auto [p, t] = tptr;
+ HANA_ASSERT(p == &a);
+ HANA_ASSERT(t == tag);
+
+ HANA_ASSERT(tptr[0] == 1);
+ HANA_ASSERT(tptr[1] == 2);
+ HANA_ASSERT(tptr[2] == 3);
+
+
+ return true;
+};
+
+
+HANA_TEST {
+ int64_t array[8] = {1,2,3,4,5,6,7,8};
+ int64_t * ptr = &array[0];
+ auto tptr = std::tagged_ptr{ptr, 0b1u};
+ int64_t sum = 0;
+ while (tptr != &array[8]) {
+ sum += *tptr;
+ ++tptr;
+ }
+ return sum;
+};
+
+HANA_TEST {
+ int64_t array[8] = {1,2,3,4,5,6,7,8};
+ auto rng = tagged_range(&array[0], 8);
+
+ static_assert(std::input_or_output_iterator<decltype(rng.begin())>);
+ static_assert(std::input_iterator<decltype(rng.begin())>);
+ static_assert(std::forward_iterator<decltype(rng.begin())>);
+ static_assert(std::bidirectional_iterator<decltype(rng.begin())>);
+ static_assert(std::random_access_iterator<decltype(rng.begin())>);
+ static_assert(std::contiguous_iterator<decltype(rng.begin())>);
+
+ static_assert(std::ranges::input_range<decltype(rng)>);
+ static_assert(std::ranges::forward_range<decltype(rng)>);
+ static_assert(std::ranges::bidirectional_range<decltype(rng)>);
+ static_assert(std::ranges::random_access_range<decltype(rng)>);
+ static_assert(std::ranges::contiguous_range<decltype(rng)>);
+ //static_assert(std::forward_iterator<tagged_range<int64_t>>);
+ //static_assert(std::ranges::bidirectional_<tagged_range<int64_t>>);
+
+ int64_t sum = 0;
+ for (int64_t v: rng) {
+ sum += v;
+ }
+ return sum;
+};
+
+HANA_TEST {
+ int a{42};
+ auto tptr = std::tagged_ptr{&a};
+
+ auto cptr = std::const_pointer_cast<const int>(tptr);
+
+ return true;
+};
+
+HANA_TEST {
+ int a{14};
+ int * b = &a;
+ auto tptr = std::tagged_pointer_cast<int, uintptr_t>(b);
+ assert(tptr.pointer() == &a);
+ return true;
+};
+
+
+
+ return 0;
+}
+
+
+#else
+int main(int, char**) { }
+#endif
diff --git a/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp.exec b/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp.exec
new file mode 100755
index 0000000000000000000000000000000000000000..da5dbfc0682484c850eba817089edf178f6db9a7
GIT binary patch
literal 16856
zcmeI4TS!!45XZm0h)R}PRz?cjLl-lSR$AMOt<tiJ;Vq#hgOAlc>UvpMcNZ at R8YSHb
zdaCFp>m}&|rjk!fQV=3Y36&)o(Nlp1Q4b+>nK^qlcQXol?LRPQzWKgy=FI%|yv{ya
zeE%&;2!kLolBSZTn}iq;7Fr3hfwY)Z%8EjV{fND)oOfeI3y$4d=5e04q*7Mfoz=0&
zTD&cGPw0LeZL at N1EK&y4u0VJl_G9x6?hiMUpkHA>602>QMZbQe87YI_PLJ0uJ>L2@
z+WED<Aw8c{4`M$iUvx5BFY!89QdVj|P!j3Yrb8yHiEf|k at YqCi%oCzNg)h=QE1BK8
zZzVsMe6As#)JHzA&y%%~T|rtw8ZCUz`Y*DIf-I!;jfk9}zayu`Q=8-NZgG>(vXPYQ
zn6D`v|B#huK6Pod{lVuKg}Z3aLK^NVnO4c#nm#RIimx$mC#&^GmSr3D$9eeoiWYt^
z_seHm4{Aa at XZ=|V)l-+9t?X2&C{L|cr#1S%aM0%yd;d_kKcKcMMS2L`7Geu2*VAYd
zg1?<C-DkcmnWTI^Y@=GkO_2uy0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X
z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH
z0TB4V2=rG?nB+4aInK}<kG4D$%`?+BvaCFx&SKM^OAnsAPNF&J6m1Gy+viYCEcE0V
zYgU%%@n(+utr(<O%?F&tBnJCvC0iS%+NIV71FCFPU2e5QB(}OdUa^vDqzf#3J=wsM
z)ZzE6)BJD`{9X%9;4e=jwKN>F_$q0FA~(}AQf3<MQCdT)Fq-)V{EBu`Cn*cff7^yF
zMr|}ayz}R0<gWt^0artV>XyDhhvIYj{YstBr*MBFv+2T((EY3_N5<tcYr*uV^>c|Q
zTP|)ow#Ps0kPGRd+kLl2oNW)^)lZmbXJ(p><<7Y8y<@#S19PFX#iw^%|J(V<nWCwJ
z<~yBh%5UBcy-~hC$@M23zp_wSsZ6hZd~L7k=;DceJ^A<M503>C$J0J;9cp^|dbDrB
PF?lY3^6Ko%FVx=;^cc)(
literal 0
HcmV?d00001
>From ee82f5441b056fb3ef56fbc92cdf0d3c56a7dec8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Fri, 11 Oct 2024 21:30:54 +0200
Subject: [PATCH 2/9] fix a bug in shift builtin, reacting to review from Louis
---
clang/lib/CodeGen/CGBuiltin.cpp | 2 +-
libcxx/include/__memory/tagged_ptr.h | 188 +++++++++---------
.../memory/tagged_ptr/tagged_ptr.pass.cpp | 36 ++--
3 files changed, 108 insertions(+), 118 deletions(-)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 7df0077dee5a80..26eddd6e7572df 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -21081,7 +21081,7 @@ RValue CodeGenFunction::EmitBuiltinTagPointerShiftOr(const CallExpr *E) {
auto *One = llvm::ConstantInt::get(IntType, 1);
llvm::Value * Mask = Builder.CreateSub(Builder.CreateShl(One, Shift), One, "mask");
- llvm::Value * MaskedValue = Builder.CreateAdd(Value, Mask, "masked_value");
+ llvm::Value * MaskedValue = Builder.CreateAnd(Value, Mask, "masked_value");
llvm::Value * PointerWithTag = Builder.CreateOr(ShiftedPointerInt, MaskedValue, "pointer_with_tag_int");
llvm::Value * Result = Builder.CreateBitOrPointerCast(PointerWithTag, Ptr->getType(), "result_ptr");
diff --git a/libcxx/include/__memory/tagged_ptr.h b/libcxx/include/__memory/tagged_ptr.h
index b0eafc633222a8..ed77de6e0543b4 100644
--- a/libcxx/include/__memory/tagged_ptr.h
+++ b/libcxx/include/__memory/tagged_ptr.h
@@ -19,45 +19,54 @@
#include <__config>
#include <__type_traits/is_trivially_copyable.h>
#include <__assert>
-#include "__bit/has_single_bit.h"
+#include <__bit/has_single_bit.h>
#include <__type_traits/rank.h>
-#include "pointer_traits.h"
+#include <__memory/pointer_traits.h>
#include <compare>
_LIBCPP_BEGIN_NAMESPACE_STD
template <typename T, typename Y> concept convertible_to_from = std::convertible_to<Y, T> && std::convertible_to<T, Y>;
-
-template <typename T> concept pointer_tagging_schema = requires(T::dirty_pointer payload, T::clean_pointer clean, T::tag_type tag) {
+
+template <typename T> concept pointer_tagging_schema =
+requires {
+ typename T::clean_pointer;
+ typename T::dirty_pointer;
+ typename T::tag_type;
+} && requires(T::dirty_pointer payload, T::clean_pointer clean, T::tag_type tag) {
requires convertible_to_from<typename T::tag_type, uintptr_t>;
requires std::is_pointer_v<typename T::clean_pointer>;
{ T::encode_pointer_with_tag(clean, tag) } noexcept -> std::same_as<typename T::dirty_pointer>;
{ T::recover_pointer(payload) } noexcept -> std::same_as<typename T::clean_pointer>;
- { T::recover_value(payload) } noexcept -> std::same_as<typename T::tag_type>;
+ { T::recover_tag(payload) } noexcept -> std::same_as<typename T::tag_type>;
};
template <typename T> concept pointer_tagging_schema_with_aliasing = pointer_tagging_schema<T> && requires(T::dirty_pointer payload) {
{ T::recover_aliasing_pointer(payload) } noexcept -> std::same_as<typename T::clean_pointer>;
};
+namespace memory {
+
// no-op schema so I can better explain how schemas work
struct no_tag {
template <typename T, typename Tag> struct schema {
using clean_pointer = T *;
using dirty_pointer = void *;
using tag_type = Tag;
+
+ static constexpr uintptr_t _mask = 0u;
- [[clang::always_inline]] static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type) noexcept {
return (dirty_pointer)_ptr;
}
- [[clang::always_inline]] static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
return (clean_pointer)_ptr;
}
- [[clang::always_inline]] static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
return (clean_pointer)_ptr;
}
- [[clang::always_inline]] static constexpr tag_type recover_value(dirty_pointer) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr tag_type recover_tag(dirty_pointer) noexcept {
return {};
}
};
@@ -66,28 +75,28 @@ struct no_tag {
// most basic schema for tagging
// it lets user to provide their own mask
template <uintptr_t Mask> struct bitmask_tag {
- static constexpr uintptr_t _mask = Mask;
-
template <typename T, typename Tag> struct schema {
using clean_pointer = T *;
using dirty_pointer = void *;
using tag_type = Tag;
+
+ static constexpr uintptr_t _mask = Mask;
- [[clang::always_inline]] static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
#if __has_builtin(__builtin_tag_pointer_mask_or)
return static_cast<dirty_pointer>(__builtin_tag_pointer_mask_or((void *)(_ptr), static_cast<uintptr_t>(_value), _mask));
#else
return reinterpret_cast<dirty_pointer>((reinterpret_cast<uintptr_t>(_ptr) & static_cast<uintptr_t>(_mask)) | (static_cast<uintptr_t>(_value) & ~static_cast<uintptr_t>(_mask)));
#endif
}
- [[clang::always_inline]] static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
#if __has_builtin(__builtin_tag_pointer_mask)
return static_cast<clean_pointer>(__builtin_tag_pointer_mask((void *)_ptr, ~_mask));
#else
return reinterpret_cast<clean_pointer>(reinterpret_cast<uintptr_t>(_ptr) & ~static_cast<uintptr_t>(_mask));
#endif
}
- [[clang::always_inline]] static constexpr tag_type recover_value(dirty_pointer _ptr) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
#if __has_builtin(__builtin_tag_pointer_mask_as_int)
return static_cast<tag_type>(__builtin_tag_pointer_mask_as_int((void *)_ptr, _mask));
#else
@@ -100,15 +109,17 @@ template <uintptr_t Mask> struct bitmask_tag {
// schema which allows only pointer of custom provided minimal alignment
// otherwise it behaves as custom mask schema
template <unsigned Alignment> struct custom_alignment_tag {
- static constexpr uintptr_t mask = (static_cast<uintptr_t>(1u) << static_cast<uintptr_t>(Alignment)) - 1ull;
+ static_assert(std::has_single_bit(Alignment), "alignment must be power of 2");
+ static constexpr uintptr_t mask = static_cast<uintptr_t>(Alignment) - 1ull;
+
template <typename T, typename Tag> struct schema: bitmask_tag<mask>::template schema<T, Tag> {
- using _underlying_schema =bitmask_tag<mask>::template schema<T, Tag>;
+ using _underlying_schema = bitmask_tag<mask>::template schema<T, Tag>;
using clean_pointer = _underlying_schema::clean_pointer;
using dirty_pointer = _underlying_schema::dirty_pointer;
using tag_type = _underlying_schema::tag_type;
- [[clang::always_inline]] static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
#if __has_builtin(__builtin_is_aligned)
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__builtin_is_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignemt for tagging");
#else
@@ -120,18 +131,17 @@ template <unsigned Alignment> struct custom_alignment_tag {
}
using _underlying_schema::recover_pointer;
- using _underlying_schema::recover_value;
+ using _underlying_schema::recover_tag;
};
};
// default scheme which gives only bits from alignment
struct alignment_low_bits_tag {
- template <typename T> static constexpr unsigned alignment = alignof(T);
- template <typename T, typename Tag> using schema = typename custom_alignment_tag<alignment<T>>::template schema<T, Tag>;
+ template <typename T, typename Tag> using schema = typename custom_alignment_tag<alignof(T)>::template schema<T, Tag>;
};
// scheme which shifts bits to left by Bits bits and gives the space for tagging
-template <unsigned Bits> struct shift_tag {
+template <unsigned Bits> struct left_shift_tag {
static constexpr unsigned _shift = Bits;
static constexpr uintptr_t _mask = (uintptr_t{1u} << _shift) - 1u;
@@ -139,22 +149,24 @@ template <unsigned Bits> struct shift_tag {
using clean_pointer = T *;
using dirty_pointer = void *;
using tag_type = Tag;
+
+ static constexpr uintptr_t _mask = left_shift_tag::_mask;
- [[clang::always_inline]] static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
#if __has_builtin(__builtin_tag_pointer_shift_or)
return static_cast<dirty_pointer>(__builtin_tag_pointer_shift_or((void *)(_ptr), (uintptr_t)_value, _shift));
#else
return reinterpret_cast<dirty_pointer>((reinterpret_cast<uintptr_t>(_ptr) << _shift) | (static_cast<uintptr_t>(_value) & ((1ull << static_cast<uintptr_t>(_shift)) - 1ull)));
#endif
}
- [[clang::always_inline]] static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
#if __has_builtin(__builtin_tag_pointer_unshift)
return static_cast<clean_pointer>(__builtin_tag_pointer_unshift((void *)_ptr, _shift));
#else
return reinterpret_cast<clean_pointer>(reinterpret_cast<uintptr_t>(_ptr) >> _shift);
#endif
}
- [[clang::always_inline]] static constexpr tag_type recover_value(dirty_pointer _ptr) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
#if __has_builtin(__builtin_tag_pointer_mask_as_int)
return static_cast<tag_type>(__builtin_tag_pointer_mask_as_int((void *)_ptr, _mask));
#else
@@ -166,7 +178,7 @@ template <unsigned Bits> struct shift_tag {
// scheme which shifts pointer to left by 8 bits and give this space as guaranteed space for tagging
struct low_byte_tag {
- template <typename T, typename Tag> using schema = typename shift_tag<8>::template schema<T, Tag>;
+ template <typename T, typename Tag> using schema = typename left_shift_tag<8>::template schema<T, Tag>;
};
// this will give user access to upper byte of pointer on aarch64
@@ -182,13 +194,13 @@ struct upper_byte_tag {
using dirty_pointer = _underlying_schema::dirty_pointer;
using tag_type = _underlying_schema::tag_type;
- [[clang::always_inline]] static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
return (clean_pointer)_ptr;
}
using _underlying_schema::encode_pointer_with_tag;
using _underlying_schema::recover_pointer;
- using _underlying_schema::recover_value;
+ using _underlying_schema::recover_tag;
};
};
@@ -202,11 +214,11 @@ struct upper_byte_shifted_tag: upper_byte_tag {
using dirty_pointer = _underlying_schema::dirty_pointer;
using tag_type = Tag;
- [[clang::always_inline]] static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+ _LIBCPP_ALWAYS_INLINE static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
return _underlying_schema::encode_pointer_with_tag(_ptr, static_cast<uintptr_t>(_value) << upper_byte_tag::_shift<T>);
}
- [[clang::always_inline]] static constexpr tag_type recover_value(dirty_pointer _ptr) noexcept {
- return static_cast<tag_type>(_underlying_schema::recover_value(_ptr) >> upper_byte_tag::_shift<T>);
+ _LIBCPP_ALWAYS_INLINE static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
+ return static_cast<tag_type>(_underlying_schema::recover_tag(_ptr) >> upper_byte_tag::_shift<T>);
}
using _underlying_schema::recover_pointer;
@@ -214,18 +226,23 @@ struct upper_byte_shifted_tag: upper_byte_tag {
};
};
-// forward declaration
-template <typename _T, typename _Tag = uintptr_t, typename _Schema = alignment_low_bits_tag> class tagged_ptr;
-
+template <unsigned Bits> struct bits_needed {
+ template <typename _T, typename _Tag> struct schema {
+ // we can automatically choose suitable schema
+ };
+};
-template <typename _Schema, typename _T, typename _Tag = uintptr_t> constexpr auto tag_ptr(_T * _ptr, _Tag _tag = {}) noexcept {
- return tagged_ptr<_T, _Tag, _Schema>{_ptr, _tag};
}
-template <typename _T, typename _Tag, typename _Schema = alignment_low_bits_tag> constexpr auto tagged_pointer_cast(typename _Schema::template schema<_T, _Tag>::dirty_pointer _ptr) noexcept -> tagged_ptr<_T, _Tag, _Schema> {
- using result_type = tagged_ptr<_T, _Tag, _Schema>;
- return result_type{typename result_type::already_tagged_tag{_ptr}};
-}
+
+// forward declaration
+template <typename _T, typename _Tag, typename _Schema> class tagged_ptr;
+
+struct already_tagged_t {
+ consteval explicit already_tagged_t(int) noexcept { }
+};
+
+constexpr auto already_tagged = already_tagged_t{0};
template <typename _Schema2, typename _T, typename _Tag, typename _Schema> constexpr auto scheme_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
return tagged_ptr<_T, _Tag, _Schema2>{in.pointer(), in.tag()};
@@ -257,7 +274,7 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
using clean_pointer = typename schema::clean_pointer;
using tag_type = typename schema::tag_type;
- using value_type = std::remove_cvref_t<decltype(*std::declval<clean_pointer>())>;
+ using element_type = typename std::pointer_traits<clean_pointer>::element_type;
using difference_type = typename std::pointer_traits<clean_pointer>::difference_type;
@@ -267,125 +284,102 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
dirty_pointer _pointer{nullptr};
- friend constexpr auto tagged_pointer_cast<_T, _Tag, _Schema>(typename _Schema::template schema<_T, _Tag>::dirty_pointer ptr) noexcept -> tagged_ptr<_T, _Tag, _Schema>;
-
- struct already_tagged_tag {
- dirty_pointer _ptr;
- };
-
- // special hidden constructor to allow constructing unsafely
- [[clang::always_inline]] constexpr tagged_ptr(already_tagged_tag _in) noexcept: _pointer{_in._ptr} { }
-
template <typename _Y, typename _T2, typename _Tag2, typename _Schema2> constexpr auto const_pointer_cast(tagged_ptr<_T2, _Tag2, _Schema2> in) noexcept -> rebind<_T>;
public:
tagged_ptr() = default;
- consteval tagged_ptr(nullptr_t) noexcept: _pointer{nullptr} { }
+ constexpr tagged_ptr(nullptr_t) noexcept: _pointer{nullptr} { }
tagged_ptr(const tagged_ptr &) = default;
tagged_ptr(tagged_ptr &&) = default;
~tagged_ptr() = default;
tagged_ptr & operator=(const tagged_ptr &) = default;
tagged_ptr & operator=(tagged_ptr &&) = default;
- [[clang::always_inline]] explicit constexpr tagged_ptr(clean_pointer _ptr, tag_type _tag = {}) noexcept: _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
+ explicit constexpr tagged_ptr(already_tagged_t, dirty_pointer _ptr) noexcept: _pointer{_ptr} { }
+
+ _LIBCPP_ALWAYS_INLINE explicit constexpr tagged_ptr(clean_pointer _ptr, tag_type _tag = {}) noexcept: _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
}
// accessors
- [[clang::always_inline]] constexpr decltype(auto) operator*() const noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr decltype(auto) operator*() const noexcept {
return *pointer();
}
- [[clang::always_inline]] constexpr clean_pointer operator->() const noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr clean_pointer operator->() const noexcept {
return pointer();
}
- template <typename...Ts> [[clang::always_inline]] [[clang::always_inline]] constexpr decltype(auto) operator[](Ts... args) const noexcept requires std::is_array_v<value_type> && (sizeof...(Ts) == std::rank_v<value_type>) {
+ template <typename...Ts> _LIBCPP_ALWAYS_INLINE constexpr decltype(auto) operator[](Ts... args) const noexcept requires std::is_array_v<element_type> && (sizeof...(Ts) == std::rank_v<element_type>) {
return (*pointer())[args...];
}
- [[clang::always_inline]] constexpr decltype(auto) operator[](difference_type diff) const noexcept requires (!std::is_array_v<value_type>) {
+ _LIBCPP_ALWAYS_INLINE constexpr decltype(auto) operator[](difference_type diff) const noexcept requires (!std::is_array_v<element_type>) {
return *(pointer() + diff);
}
// swap
- [[clang::always_inline]] friend constexpr void swap(tagged_ptr & lhs, tagged_ptr & rhs) noexcept {
+ _LIBCPP_ALWAYS_INLINE friend constexpr void swap(tagged_ptr & lhs, tagged_ptr & rhs) noexcept {
std::swap(lhs._pointer, rhs._pointer);
}
// modifiers for tag
- [[clang::always_inline]] constexpr auto & set(tag_type new_tag) noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr auto & set(tag_type new_tag) noexcept {
// this is here so I can avoid checks
// TODO we should be able to check what bits available
_pointer = schema::encode_pointer_with_tag(pointer(), new_tag);
return *this;
}
-
- [[clang::always_inline]] constexpr auto & set_union(tag_type addition) noexcept {
- return set(tag() | addition);
- }
-
- [[clang::always_inline]] constexpr auto & set_difference(tag_type mask) noexcept {
- return set(tag() & (~static_cast<uintptr_t>(mask)));
- }
-
- [[clang::always_inline]] constexpr auto & set_intersection(tag_type mask) noexcept {
- return set(tag() & mask);
- }
-
- [[clang::always_inline]] constexpr auto & set_all() noexcept {
- return set(static_cast<tag_type>(0xFFFFFFFF'FFFFFFFFull));
- }
// modifiers for pointer
- [[clang::always_inline]] constexpr auto & operator++() noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr auto & operator++() noexcept {
_pointer = tagged_ptr{pointer()+1u, tag()}._pointer;
return *this;
}
- [[clang::always_inline]] constexpr auto operator++(int) noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr auto operator++(int) noexcept {
auto copy = auto(*this);
this->operator++();
return copy;
}
- [[clang::always_inline]] constexpr auto & operator+=(difference_type diff) noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr auto & operator+=(difference_type diff) noexcept {
_pointer = tagged_ptr{pointer()+diff, tag()}._pointer;
return *this;
}
- [[clang::always_inline]] friend constexpr auto operator+(tagged_ptr lhs, difference_type diff) noexcept {
+ _LIBCPP_ALWAYS_INLINE friend constexpr auto operator+(tagged_ptr lhs, difference_type diff) noexcept {
lhs += diff;
return lhs;
}
- [[clang::always_inline]] friend constexpr auto operator+(difference_type diff, tagged_ptr rhs) noexcept {
+ _LIBCPP_ALWAYS_INLINE friend constexpr auto operator+(difference_type diff, tagged_ptr rhs) noexcept {
rhs += diff;
return rhs;
}
- [[clang::always_inline]] friend constexpr auto operator-(tagged_ptr lhs, difference_type diff) noexcept {
+ _LIBCPP_ALWAYS_INLINE friend constexpr auto operator-(tagged_ptr lhs, difference_type diff) noexcept {
lhs -= diff;
return lhs;
}
- [[clang::always_inline]] friend constexpr auto operator-(difference_type diff, tagged_ptr rhs) noexcept {
+ _LIBCPP_ALWAYS_INLINE friend constexpr auto operator-(difference_type diff, tagged_ptr rhs) noexcept {
rhs -= diff;
return rhs;
}
- [[clang::always_inline]] constexpr auto & operator-=(difference_type diff) noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr auto & operator-=(difference_type diff) noexcept {
_pointer = tagged_ptr{pointer()-diff, tag()}._pointer;
return *this;
}
- [[clang::always_inline]] constexpr auto & operator--() noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr auto & operator--() noexcept {
_pointer = tagged_ptr{pointer()-1u, tag()}._pointer;
return *this;
}
- [[clang::always_inline]] constexpr auto operator--(int) noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr auto operator--(int) noexcept {
auto copy = auto(*this);
this->operator--();
return copy;
@@ -400,7 +394,7 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
static constexpr bool support_aliasing_masking = pointer_tagging_schema_with_aliasing<schema>;
- [[clang::always_inline]] constexpr clean_pointer aliasing_pointer() const noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr clean_pointer aliasing_pointer() const noexcept {
if constexpr (support_aliasing_masking) {
if !consteval {
return schema::recover_aliasing_pointer(_pointer);
@@ -410,12 +404,12 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
return schema::recover_pointer(_pointer);
}
- [[clang::always_inline]] constexpr clean_pointer pointer() const noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr clean_pointer pointer() const noexcept {
return schema::recover_pointer(_pointer);
}
- [[clang::always_inline]] constexpr tag_type tag() const noexcept {
- return schema::recover_value(_pointer);
+ _LIBCPP_ALWAYS_INLINE constexpr tag_type tag() const noexcept {
+ return schema::recover_tag(_pointer);
}
template <std::size_t I> [[nodiscard, clang::always_inline]] friend constexpr decltype(auto) get(tagged_ptr _pair) noexcept {
@@ -427,16 +421,16 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
}
}
- [[clang::always_inline]] constexpr explicit operator bool() const noexcept {
+ _LIBCPP_ALWAYS_INLINE constexpr explicit operator bool() const noexcept {
return pointer() != nullptr;
}
- [[clang::always_inline]] friend constexpr ptrdiff_t operator-(tagged_ptr lhs, tagged_ptr rhs) noexcept {
+ _LIBCPP_ALWAYS_INLINE friend constexpr ptrdiff_t operator-(tagged_ptr lhs, tagged_ptr rhs) noexcept {
return lhs.pointer() - rhs.pointer();
}
// comparison operators
- [[clang::always_inline]] friend bool operator==(tagged_ptr, tagged_ptr) = default;
+ _LIBCPP_ALWAYS_INLINE friend bool operator==(tagged_ptr, tagged_ptr) = default;
struct _compare_object {
clean_pointer pointer;
@@ -445,16 +439,16 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
friend auto operator<=>(_compare_object, _compare_object) = default;
};
- [[clang::always_inline]] friend constexpr auto operator<=>(tagged_ptr lhs, tagged_ptr rhs) noexcept {
+ _LIBCPP_ALWAYS_INLINE friend constexpr auto operator<=>(tagged_ptr lhs, tagged_ptr rhs) noexcept {
return _compare_object{lhs.pointer(), lhs.tag()} <=> _compare_object{rhs.pointer(), rhs.tag()};
}
- [[clang::always_inline]] friend constexpr bool operator==(tagged_ptr lhs, clean_pointer rhs) noexcept {
+ _LIBCPP_ALWAYS_INLINE friend constexpr bool operator==(tagged_ptr lhs, clean_pointer rhs) noexcept {
return lhs.pointer() == rhs;
}
- [[clang::always_inline]] friend constexpr auto operator<=>(tagged_ptr lhs, clean_pointer rhs) noexcept {
+ _LIBCPP_ALWAYS_INLINE friend constexpr auto operator<=>(tagged_ptr lhs, clean_pointer rhs) noexcept {
return lhs.pointer() <=> rhs;
}
- [[clang::always_inline]] friend constexpr bool operator==(tagged_ptr lhs, nullptr_t) noexcept {
+ _LIBCPP_ALWAYS_INLINE friend constexpr bool operator==(tagged_ptr lhs, nullptr_t) noexcept {
return lhs.pointer() == nullptr;
}
};
@@ -472,7 +466,7 @@ struct _LIBCPP_TEMPLATE_VIS iterator_traits<tagged_ptr<_T, _Tag, _Schema>> {
using iterator_category = std::random_access_iterator_tag;
using iterator_concept = std::contiguous_iterator_tag;
- using value_type = _tagged_ptr::value_type;
+ using value_type = _tagged_ptr::element_type;
using reference = value_type &;
using pointer = _tagged_ptr::clean_pointer;
using difference_type = _tagged_ptr::difference_type;
@@ -494,10 +488,6 @@ struct _LIBCPP_TEMPLATE_VIS pointer_traits<tagged_ptr<_T, _Tag, _Schema>> {
}
};
-// we are defaulting always to low_bits schema
-template <typename _T> tagged_ptr(_T *) -> tagged_ptr<_T>;
-template <typename _T, typename _Tag> tagged_ptr(_T *, _Tag) -> tagged_ptr<_T, _Tag>;
-
// support for tuple protocol so we can split tagged pointer to structured bindings:
// auto [ptr, tag] = tagged_ptr
template <typename _T, typename _Tag, typename _Schema>
diff --git a/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp b/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
index 4b935ed69905fd..1e0dae4cf0b5f9 100644
--- a/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
+++ b/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
@@ -33,7 +33,8 @@ template <bool Expected> struct test_helper {
#define HANA_ASSERT(expr) do { if (!static_cast<bool>(expr)) { if consteval { throw false; } return false; }} while (false)
template <typename T> struct tagged_range {
- using scheme = std::no_tag;
+ using scheme = std::memory::no_tag;
+ using iterator = std::tagged_ptr<const T, bool, scheme>;
const T * start;
size_t size;
@@ -41,22 +42,21 @@ template <typename T> struct tagged_range {
constexpr tagged_range(const T * in, std::size_t sz) noexcept: start{in}, size{sz} { }
constexpr auto begin() const {
- return std::tag_ptr<scheme>(start);
+ return iterator(start, false);
}
constexpr auto end() const {
- return std::tag_ptr<scheme>(start+size);
+ return iterator(start+size, false);
}
};
-#if TEST_STD_VER >= 26
int main(int, char**) {
-
+
HANA_TEST {
int64_t a = 42;
- uintptr_t tag = 0b101u;
- auto tptr = std::tagged_ptr(&a, tag);
+ uintptr_t tag = 0b11u;
+ auto tptr = std::tagged_ptr<int64_t, uintptr_t, std::memory::alignment_low_bits_tag>(&a, tag);
HANA_ASSERT(tptr.unsafe_dirty_pointer() != &a);
@@ -74,7 +74,7 @@ HANA_TEST {
int64_t a[3] = {1,2,3};
uintptr_t tag = 0b11000111u;
- auto tptr = std::tag_ptr<std::low_byte_tag>(&a, tag);
+ auto tptr = std::tagged_ptr<int64_t[3], uintptr_t, std::memory::low_byte_tag>(&a, tag);
auto * original = tptr.pointer();
HANA_ASSERT(tag == tptr.tag());
@@ -96,7 +96,7 @@ HANA_TEST {
HANA_TEST {
int64_t array[8] = {1,2,3,4,5,6,7,8};
int64_t * ptr = &array[0];
- auto tptr = std::tagged_ptr{ptr, 0b1u};
+ auto tptr = std::tagged_ptr<int64_t, unsigned, std::memory::alignment_low_bits_tag>{ptr, 0b1u};
int64_t sum = 0;
while (tptr != &array[8]) {
sum += *tptr;
@@ -114,13 +114,13 @@ HANA_TEST {
static_assert(std::forward_iterator<decltype(rng.begin())>);
static_assert(std::bidirectional_iterator<decltype(rng.begin())>);
static_assert(std::random_access_iterator<decltype(rng.begin())>);
- static_assert(std::contiguous_iterator<decltype(rng.begin())>);
+ //static_assert(std::contiguous_iterator<decltype(rng.begin())>);
static_assert(std::ranges::input_range<decltype(rng)>);
static_assert(std::ranges::forward_range<decltype(rng)>);
static_assert(std::ranges::bidirectional_range<decltype(rng)>);
static_assert(std::ranges::random_access_range<decltype(rng)>);
- static_assert(std::ranges::contiguous_range<decltype(rng)>);
+ //static_assert(std::ranges::contiguous_range<decltype(rng)>);
//static_assert(std::forward_iterator<tagged_range<int64_t>>);
//static_assert(std::ranges::bidirectional_<tagged_range<int64_t>>);
@@ -133,27 +133,27 @@ HANA_TEST {
HANA_TEST {
int a{42};
- auto tptr = std::tagged_ptr{&a};
+ auto tptr = std::tagged_ptr<int, bool, std::memory::alignment_low_bits_tag>{&a, false};
auto cptr = std::const_pointer_cast<const int>(tptr);
+ HANA_ASSERT(cptr.tag() == false);
+ HANA_ASSERT(cptr.pointer() == &a);
+
return true;
};
HANA_TEST {
int a{14};
int * b = &a;
- auto tptr = std::tagged_pointer_cast<int, uintptr_t>(b);
- assert(tptr.pointer() == &a);
+ auto tptr = std::tagged_ptr<int, uintptr_t, std::memory::alignment_low_bits_tag>(b);
+ HANA_ASSERT(tptr.pointer() == &a);
return true;
};
+
return 0;
}
-
-#else
-int main(int, char**) { }
-#endif
>From 875a922f35841903fb8dc14578ea8059903015b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Fri, 11 Oct 2024 21:31:33 +0200
Subject: [PATCH 3/9] remove all the force inlining
---
libcxx/include/__memory/tagged_ptr.h | 82 ++++++++++++++--------------
1 file changed, 41 insertions(+), 41 deletions(-)
diff --git a/libcxx/include/__memory/tagged_ptr.h b/libcxx/include/__memory/tagged_ptr.h
index ed77de6e0543b4..316921b283c7b9 100644
--- a/libcxx/include/__memory/tagged_ptr.h
+++ b/libcxx/include/__memory/tagged_ptr.h
@@ -57,16 +57,16 @@ struct no_tag {
static constexpr uintptr_t _mask = 0u;
- _LIBCPP_ALWAYS_INLINE static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type) noexcept {
+ static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type) noexcept {
return (dirty_pointer)_ptr;
}
- _LIBCPP_ALWAYS_INLINE static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
+ static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
return (clean_pointer)_ptr;
}
- _LIBCPP_ALWAYS_INLINE static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
+ static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
return (clean_pointer)_ptr;
}
- _LIBCPP_ALWAYS_INLINE static constexpr tag_type recover_tag(dirty_pointer) noexcept {
+ static constexpr tag_type recover_tag(dirty_pointer) noexcept {
return {};
}
};
@@ -82,21 +82,21 @@ template <uintptr_t Mask> struct bitmask_tag {
static constexpr uintptr_t _mask = Mask;
- _LIBCPP_ALWAYS_INLINE static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+ static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
#if __has_builtin(__builtin_tag_pointer_mask_or)
return static_cast<dirty_pointer>(__builtin_tag_pointer_mask_or((void *)(_ptr), static_cast<uintptr_t>(_value), _mask));
#else
return reinterpret_cast<dirty_pointer>((reinterpret_cast<uintptr_t>(_ptr) & static_cast<uintptr_t>(_mask)) | (static_cast<uintptr_t>(_value) & ~static_cast<uintptr_t>(_mask)));
#endif
}
- _LIBCPP_ALWAYS_INLINE static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
+ static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
#if __has_builtin(__builtin_tag_pointer_mask)
return static_cast<clean_pointer>(__builtin_tag_pointer_mask((void *)_ptr, ~_mask));
#else
return reinterpret_cast<clean_pointer>(reinterpret_cast<uintptr_t>(_ptr) & ~static_cast<uintptr_t>(_mask));
#endif
}
- _LIBCPP_ALWAYS_INLINE static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
+ static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
#if __has_builtin(__builtin_tag_pointer_mask_as_int)
return static_cast<tag_type>(__builtin_tag_pointer_mask_as_int((void *)_ptr, _mask));
#else
@@ -119,7 +119,7 @@ template <unsigned Alignment> struct custom_alignment_tag {
using dirty_pointer = _underlying_schema::dirty_pointer;
using tag_type = _underlying_schema::tag_type;
- _LIBCPP_ALWAYS_INLINE static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+ static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
#if __has_builtin(__builtin_is_aligned)
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__builtin_is_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignemt for tagging");
#else
@@ -152,21 +152,21 @@ template <unsigned Bits> struct left_shift_tag {
static constexpr uintptr_t _mask = left_shift_tag::_mask;
- _LIBCPP_ALWAYS_INLINE static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+ static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
#if __has_builtin(__builtin_tag_pointer_shift_or)
return static_cast<dirty_pointer>(__builtin_tag_pointer_shift_or((void *)(_ptr), (uintptr_t)_value, _shift));
#else
return reinterpret_cast<dirty_pointer>((reinterpret_cast<uintptr_t>(_ptr) << _shift) | (static_cast<uintptr_t>(_value) & ((1ull << static_cast<uintptr_t>(_shift)) - 1ull)));
#endif
}
- _LIBCPP_ALWAYS_INLINE static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
+ static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
#if __has_builtin(__builtin_tag_pointer_unshift)
return static_cast<clean_pointer>(__builtin_tag_pointer_unshift((void *)_ptr, _shift));
#else
return reinterpret_cast<clean_pointer>(reinterpret_cast<uintptr_t>(_ptr) >> _shift);
#endif
}
- _LIBCPP_ALWAYS_INLINE static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
+ static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
#if __has_builtin(__builtin_tag_pointer_mask_as_int)
return static_cast<tag_type>(__builtin_tag_pointer_mask_as_int((void *)_ptr, _mask));
#else
@@ -194,7 +194,7 @@ struct upper_byte_tag {
using dirty_pointer = _underlying_schema::dirty_pointer;
using tag_type = _underlying_schema::tag_type;
- _LIBCPP_ALWAYS_INLINE static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
+ static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
return (clean_pointer)_ptr;
}
@@ -214,10 +214,10 @@ struct upper_byte_shifted_tag: upper_byte_tag {
using dirty_pointer = _underlying_schema::dirty_pointer;
using tag_type = Tag;
- _LIBCPP_ALWAYS_INLINE static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+ static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
return _underlying_schema::encode_pointer_with_tag(_ptr, static_cast<uintptr_t>(_value) << upper_byte_tag::_shift<T>);
}
- _LIBCPP_ALWAYS_INLINE static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
+ static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
return static_cast<tag_type>(_underlying_schema::recover_tag(_ptr) >> upper_byte_tag::_shift<T>);
}
@@ -297,35 +297,35 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
explicit constexpr tagged_ptr(already_tagged_t, dirty_pointer _ptr) noexcept: _pointer{_ptr} { }
- _LIBCPP_ALWAYS_INLINE explicit constexpr tagged_ptr(clean_pointer _ptr, tag_type _tag = {}) noexcept: _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
+ explicit constexpr tagged_ptr(clean_pointer _ptr, tag_type _tag = {}) noexcept: _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
}
// accessors
- _LIBCPP_ALWAYS_INLINE constexpr decltype(auto) operator*() const noexcept {
+ constexpr decltype(auto) operator*() const noexcept {
return *pointer();
}
- _LIBCPP_ALWAYS_INLINE constexpr clean_pointer operator->() const noexcept {
+ constexpr clean_pointer operator->() const noexcept {
return pointer();
}
- template <typename...Ts> _LIBCPP_ALWAYS_INLINE constexpr decltype(auto) operator[](Ts... args) const noexcept requires std::is_array_v<element_type> && (sizeof...(Ts) == std::rank_v<element_type>) {
+ template <typename...Ts> constexpr decltype(auto) operator[](Ts... args) const noexcept requires std::is_array_v<element_type> && (sizeof...(Ts) == std::rank_v<element_type>) {
return (*pointer())[args...];
}
- _LIBCPP_ALWAYS_INLINE constexpr decltype(auto) operator[](difference_type diff) const noexcept requires (!std::is_array_v<element_type>) {
+ constexpr decltype(auto) operator[](difference_type diff) const noexcept requires (!std::is_array_v<element_type>) {
return *(pointer() + diff);
}
// swap
- _LIBCPP_ALWAYS_INLINE friend constexpr void swap(tagged_ptr & lhs, tagged_ptr & rhs) noexcept {
+ friend constexpr void swap(tagged_ptr & lhs, tagged_ptr & rhs) noexcept {
std::swap(lhs._pointer, rhs._pointer);
}
// modifiers for tag
- _LIBCPP_ALWAYS_INLINE constexpr auto & set(tag_type new_tag) noexcept {
+ constexpr auto & set(tag_type new_tag) noexcept {
// this is here so I can avoid checks
// TODO we should be able to check what bits available
_pointer = schema::encode_pointer_with_tag(pointer(), new_tag);
@@ -333,53 +333,53 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
}
// modifiers for pointer
- _LIBCPP_ALWAYS_INLINE constexpr auto & operator++() noexcept {
+ constexpr auto & operator++() noexcept {
_pointer = tagged_ptr{pointer()+1u, tag()}._pointer;
return *this;
}
- _LIBCPP_ALWAYS_INLINE constexpr auto operator++(int) noexcept {
+ constexpr auto operator++(int) noexcept {
auto copy = auto(*this);
this->operator++();
return copy;
}
- _LIBCPP_ALWAYS_INLINE constexpr auto & operator+=(difference_type diff) noexcept {
+ constexpr auto & operator+=(difference_type diff) noexcept {
_pointer = tagged_ptr{pointer()+diff, tag()}._pointer;
return *this;
}
- _LIBCPP_ALWAYS_INLINE friend constexpr auto operator+(tagged_ptr lhs, difference_type diff) noexcept {
+ friend constexpr auto operator+(tagged_ptr lhs, difference_type diff) noexcept {
lhs += diff;
return lhs;
}
- _LIBCPP_ALWAYS_INLINE friend constexpr auto operator+(difference_type diff, tagged_ptr rhs) noexcept {
+ friend constexpr auto operator+(difference_type diff, tagged_ptr rhs) noexcept {
rhs += diff;
return rhs;
}
- _LIBCPP_ALWAYS_INLINE friend constexpr auto operator-(tagged_ptr lhs, difference_type diff) noexcept {
+ friend constexpr auto operator-(tagged_ptr lhs, difference_type diff) noexcept {
lhs -= diff;
return lhs;
}
- _LIBCPP_ALWAYS_INLINE friend constexpr auto operator-(difference_type diff, tagged_ptr rhs) noexcept {
+ friend constexpr auto operator-(difference_type diff, tagged_ptr rhs) noexcept {
rhs -= diff;
return rhs;
}
- _LIBCPP_ALWAYS_INLINE constexpr auto & operator-=(difference_type diff) noexcept {
+ constexpr auto & operator-=(difference_type diff) noexcept {
_pointer = tagged_ptr{pointer()-diff, tag()}._pointer;
return *this;
}
- _LIBCPP_ALWAYS_INLINE constexpr auto & operator--() noexcept {
+ constexpr auto & operator--() noexcept {
_pointer = tagged_ptr{pointer()-1u, tag()}._pointer;
return *this;
}
- _LIBCPP_ALWAYS_INLINE constexpr auto operator--(int) noexcept {
+ constexpr auto operator--(int) noexcept {
auto copy = auto(*this);
this->operator--();
return copy;
@@ -394,7 +394,7 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
static constexpr bool support_aliasing_masking = pointer_tagging_schema_with_aliasing<schema>;
- _LIBCPP_ALWAYS_INLINE constexpr clean_pointer aliasing_pointer() const noexcept {
+ constexpr clean_pointer aliasing_pointer() const noexcept {
if constexpr (support_aliasing_masking) {
if !consteval {
return schema::recover_aliasing_pointer(_pointer);
@@ -404,11 +404,11 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
return schema::recover_pointer(_pointer);
}
- _LIBCPP_ALWAYS_INLINE constexpr clean_pointer pointer() const noexcept {
+ constexpr clean_pointer pointer() const noexcept {
return schema::recover_pointer(_pointer);
}
- _LIBCPP_ALWAYS_INLINE constexpr tag_type tag() const noexcept {
+ constexpr tag_type tag() const noexcept {
return schema::recover_tag(_pointer);
}
@@ -421,16 +421,16 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
}
}
- _LIBCPP_ALWAYS_INLINE constexpr explicit operator bool() const noexcept {
+ constexpr explicit operator bool() const noexcept {
return pointer() != nullptr;
}
- _LIBCPP_ALWAYS_INLINE friend constexpr ptrdiff_t operator-(tagged_ptr lhs, tagged_ptr rhs) noexcept {
+ friend constexpr ptrdiff_t operator-(tagged_ptr lhs, tagged_ptr rhs) noexcept {
return lhs.pointer() - rhs.pointer();
}
// comparison operators
- _LIBCPP_ALWAYS_INLINE friend bool operator==(tagged_ptr, tagged_ptr) = default;
+ friend bool operator==(tagged_ptr, tagged_ptr) = default;
struct _compare_object {
clean_pointer pointer;
@@ -439,16 +439,16 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
friend auto operator<=>(_compare_object, _compare_object) = default;
};
- _LIBCPP_ALWAYS_INLINE friend constexpr auto operator<=>(tagged_ptr lhs, tagged_ptr rhs) noexcept {
+ friend constexpr auto operator<=>(tagged_ptr lhs, tagged_ptr rhs) noexcept {
return _compare_object{lhs.pointer(), lhs.tag()} <=> _compare_object{rhs.pointer(), rhs.tag()};
}
- _LIBCPP_ALWAYS_INLINE friend constexpr bool operator==(tagged_ptr lhs, clean_pointer rhs) noexcept {
+ friend constexpr bool operator==(tagged_ptr lhs, clean_pointer rhs) noexcept {
return lhs.pointer() == rhs;
}
- _LIBCPP_ALWAYS_INLINE friend constexpr auto operator<=>(tagged_ptr lhs, clean_pointer rhs) noexcept {
+ friend constexpr auto operator<=>(tagged_ptr lhs, clean_pointer rhs) noexcept {
return lhs.pointer() <=> rhs;
}
- _LIBCPP_ALWAYS_INLINE friend constexpr bool operator==(tagged_ptr lhs, nullptr_t) noexcept {
+ friend constexpr bool operator==(tagged_ptr lhs, nullptr_t) noexcept {
return lhs.pointer() == nullptr;
}
};
>From 5702954f9be86aa96701fd3f89bcfb2df257a892 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Fri, 11 Oct 2024 22:49:41 +0200
Subject: [PATCH 4/9] main tagged_ptr is not available when pointer has not
enough space, but it can be passed with overalignment tag or
known_unused_bits tag
---
libcxx/include/__memory/pointer_traits.h | 12 ++--
libcxx/include/__memory/tagged_ptr.h | 78 +++++++++++++++++++++---
2 files changed, 77 insertions(+), 13 deletions(-)
diff --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h
index 983bbe25680866..74723d5b4d784b 100644
--- a/libcxx/include/__memory/pointer_traits.h
+++ b/libcxx/include/__memory/pointer_traits.h
@@ -163,12 +163,12 @@ struct _LIBCPP_TEMPLATE_VIS pointer_traits<_Tp*> {
#endif
#if _LIBCPP_STD_VER >= 26
- template <unsigned OverAlignment = alignof(element_type)>
- static constexpr uintptr_t available_bits = ((1ull << OverAlignment) - 1ull);
-
- // what to do with upper bits we can't detect at compile-time?
- template <unsigned OverAlignment = alignof(element_type)>
- static constexpr uintptr_t significant_bits = ~available_bits<OverAlignment>;
+#if __aarch64__
+ static constexpr uintptr_t _upper_bits = 0xFFull << (sizeof(void *) * 8ull) - 8ull;
+#else
+ static constexpr uintptr_t _upper_bits = 0ull;
+#endif
+ static constexpr uintptr_t unused_bits = ((1ull << (alignof(element_type) - 1ull)) - 1ull) | _upper_bits;
#endif
private:
diff --git a/libcxx/include/__memory/tagged_ptr.h b/libcxx/include/__memory/tagged_ptr.h
index 316921b283c7b9..e9a48b7e68812b 100644
--- a/libcxx/include/__memory/tagged_ptr.h
+++ b/libcxx/include/__memory/tagged_ptr.h
@@ -46,6 +46,8 @@ template <typename T> concept pointer_tagging_schema_with_aliasing = pointer_tag
{ T::recover_aliasing_pointer(payload) } noexcept -> std::same_as<typename T::clean_pointer>;
};
+template <size_t Alignment> requires (std::has_single_bit(Alignment)) constexpr uintptr_t alignment_free_bits = static_cast<uintptr_t>(Alignment) - 1ull;
+
namespace memory {
// no-op schema so I can better explain how schemas work
@@ -56,6 +58,7 @@ struct no_tag {
using tag_type = Tag;
static constexpr uintptr_t _mask = 0u;
+ static constexpr auto used_bits = _mask;
static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type) noexcept {
return (dirty_pointer)_ptr;
@@ -81,6 +84,8 @@ template <uintptr_t Mask> struct bitmask_tag {
using tag_type = Tag;
static constexpr uintptr_t _mask = Mask;
+
+ static constexpr auto used_bits = _mask;
static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
#if __has_builtin(__builtin_tag_pointer_mask_or)
@@ -110,7 +115,7 @@ template <uintptr_t Mask> struct bitmask_tag {
// otherwise it behaves as custom mask schema
template <unsigned Alignment> struct custom_alignment_tag {
static_assert(std::has_single_bit(Alignment), "alignment must be power of 2");
- static constexpr uintptr_t mask = static_cast<uintptr_t>(Alignment) - 1ull;
+ static constexpr uintptr_t mask = alignment_free_bits<Alignment>;
template <typename T, typename Tag> struct schema: bitmask_tag<mask>::template schema<T, Tag> {
using _underlying_schema = bitmask_tag<mask>::template schema<T, Tag>;
@@ -119,9 +124,11 @@ template <unsigned Alignment> struct custom_alignment_tag {
using dirty_pointer = _underlying_schema::dirty_pointer;
using tag_type = _underlying_schema::tag_type;
+ static constexpr auto used_bits = _underlying_schema::used_bits;
+
static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
#if __has_builtin(__builtin_is_aligned)
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__builtin_is_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignemt for tagging");
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__builtin_is_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignment for tagging");
#else
if !consteval {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(reinterpret_cast<uintptr_t>(std::addressof(_ptr)) % Alignment == 0, "Pointer must be aligned by provided alignemt for tagging");
@@ -150,7 +157,8 @@ template <unsigned Bits> struct left_shift_tag {
using dirty_pointer = void *;
using tag_type = Tag;
- static constexpr uintptr_t _mask = left_shift_tag::_mask;
+ // what to do here?
+ static constexpr uintptr_t used_bits = left_shift_tag::_mask;
static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
#if __has_builtin(__builtin_tag_pointer_shift_or)
@@ -193,6 +201,8 @@ struct upper_byte_tag {
using clean_pointer = _underlying_schema::clean_pointer;
using dirty_pointer = _underlying_schema::dirty_pointer;
using tag_type = _underlying_schema::tag_type;
+
+ static constexpr uintptr_t used_bits = _mask<T>;
static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
return (clean_pointer)_ptr;
@@ -226,9 +236,30 @@ struct upper_byte_shifted_tag: upper_byte_tag {
};
};
-template <unsigned Bits> struct bits_needed {
+// "clever" schema which can use any bits
+template <size_t Bits> struct bits_needed_tag {
template <typename _T, typename _Tag> struct schema {
- // we can automatically choose suitable schema
+ static constexpr auto _available_bits = std::pointer_traits<_T *>::unused_bits;
+ static constexpr auto _mask = (uintptr_t{1} << Bits) - uintptr_t{1}; /* FIXME: use lowest N bits from _available_bits */
+
+ using _underlying_schema = bitmask_tag<_mask>::template schema<_T, uintptr_t>;
+
+ static constexpr auto used_bits = _mask;
+
+ using clean_pointer = _underlying_schema::clean_pointer;
+ using dirty_pointer = _underlying_schema::dirty_pointer;
+ using tag_type = _Tag;
+
+ static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
+ // TODO something clever :)
+ return _underlying_schema::encode_pointer_with_tag(_ptr, _value);
+ }
+ static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
+ return _underlying_schema::recover_pointer(_ptr);
+ }
+ static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
+ return static_cast<tag_type>(_underlying_schema::recover_tag(_ptr));
+ }
};
};
@@ -242,8 +273,20 @@ struct already_tagged_t {
consteval explicit already_tagged_t(int) noexcept { }
};
+template <size_t Alignment> requires (std::has_single_bit(Alignment)) struct overaligned_pointer_t {
+ consteval explicit overaligned_pointer_t(int) noexcept { }
+};
+
+template <uintptr_t FreeBits> struct known_unused_bits_t {
+ consteval explicit known_unused_bits_t(int) noexcept { }
+};
+
constexpr auto already_tagged = already_tagged_t{0};
+template <size_t Alignment> requires (std::has_single_bit(Alignment)) constexpr auto overaligned_pointer = overaligned_pointer_t<Alignment>{0};
+
+template <uintptr_t FreeBits> auto known_unused_bits = known_unused_bits_t<FreeBits>{0};
+
template <typename _Schema2, typename _T, typename _Tag, typename _Schema> constexpr auto scheme_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
return tagged_ptr<_T, _Tag, _Schema2>{in.pointer(), in.tag()};
}
@@ -268,16 +311,21 @@ template <typename _Y, typename _T, typename _Tag, typename _Schema> auto reinte
// wrapper class containing the pointer value and provides access
template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
+
public:
using schema = typename _Schema::template schema<_T, _Tag>;
using dirty_pointer = typename schema::dirty_pointer;
using clean_pointer = typename schema::clean_pointer;
+
+ static constexpr auto clean_pointer_free_bits = std::pointer_traits<clean_pointer>::unused_bits;
+ static constexpr auto dirty_pointer_free_bits = clean_pointer_free_bits & schema::used_bits;
+ template <uintptr_t FreeBits> static constexpr bool has_all_bits_available = (schema::used_bits & FreeBits) == schema::used_bits;
+
using tag_type = typename schema::tag_type;
using element_type = typename std::pointer_traits<clean_pointer>::element_type;
using difference_type = typename std::pointer_traits<clean_pointer>::difference_type;
-
template <typename _Y> using rebind = tagged_ptr<_Y, _Tag, _Schema>;
private:
@@ -297,10 +345,24 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
explicit constexpr tagged_ptr(already_tagged_t, dirty_pointer _ptr) noexcept: _pointer{_ptr} { }
- explicit constexpr tagged_ptr(clean_pointer _ptr, tag_type _tag = {}) noexcept: _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
+ explicit constexpr tagged_ptr(clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
+ _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
+ _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
+ }
+
+ // construct which allows to communicate overaligned pointer to push over-aligned pointer
+ template <size_t Alignment> explicit constexpr tagged_ptr(overaligned_pointer_t<Alignment>, clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits | (std::alignment_free_bits<Alignment>)>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
+
+ _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
+ _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
+ }
+
+ // construct which allows manually provide known free bits (platform specific)
+ template <uintptr_t AdditionalFreeBits> explicit constexpr tagged_ptr(known_unused_bits_t<AdditionalFreeBits>, clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits | AdditionalFreeBits>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
}
+
// accessors
constexpr decltype(auto) operator*() const noexcept {
@@ -482,6 +544,8 @@ struct _LIBCPP_TEMPLATE_VIS pointer_traits<tagged_ptr<_T, _Tag, _Schema>> {
template <typename _Up> using rebind = typename _tagged_ptr::template rebind<_Up>;
+ static constexpr uintptr_t unused_bits = _tagged_ptr::dirty_pointer_free_bits;
+
public:
_LIBCPP_HIDE_FROM_ABI constexpr static _tagged_ptr pointer_to(pointer ptr) _NOEXCEPT {
return _tagged_ptr{ptr};
>From 72435e2619a12e92bd5c48c37c0b3bb170028678 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Fri, 11 Oct 2024 23:57:02 +0200
Subject: [PATCH 5/9] additional safety
---
libcxx/include/__memory/pointer_traits.h | 2 +-
libcxx/include/__memory/tagged_ptr.h | 17 ++++--
.../memory/tagged_ptr/tagged_ptr.pass.cpp | 54 ++++++++++++++++++-
3 files changed, 66 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h
index 74723d5b4d784b..c8b34e58646831 100644
--- a/libcxx/include/__memory/pointer_traits.h
+++ b/libcxx/include/__memory/pointer_traits.h
@@ -168,7 +168,7 @@ struct _LIBCPP_TEMPLATE_VIS pointer_traits<_Tp*> {
#else
static constexpr uintptr_t _upper_bits = 0ull;
#endif
- static constexpr uintptr_t unused_bits = ((1ull << (alignof(element_type) - 1ull)) - 1ull) | _upper_bits;
+ static constexpr uintptr_t unused_bits = (alignof(element_type) - 1ull) | _upper_bits;
#endif
private:
diff --git a/libcxx/include/__memory/tagged_ptr.h b/libcxx/include/__memory/tagged_ptr.h
index e9a48b7e68812b..c46c43f032b5dc 100644
--- a/libcxx/include/__memory/tagged_ptr.h
+++ b/libcxx/include/__memory/tagged_ptr.h
@@ -131,7 +131,7 @@ template <unsigned Alignment> struct custom_alignment_tag {
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__builtin_is_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignment for tagging");
#else
if !consteval {
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(reinterpret_cast<uintptr_t>(std::addressof(_ptr)) % Alignment == 0, "Pointer must be aligned by provided alignemt for tagging");
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(reinterpret_cast<uintptr_t>(_ptr) % Alignment == 0, "Pointer must be aligned by provided alignemt for tagging");
}
#endif
return _underlying_schema::encode_pointer_with_tag(_ptr, _value);
@@ -158,7 +158,7 @@ template <unsigned Bits> struct left_shift_tag {
using tag_type = Tag;
// what to do here?
- static constexpr uintptr_t used_bits = left_shift_tag::_mask;
+ static constexpr uintptr_t used_bits = ~((~uintptr_t{0}) >> _shift);
static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
#if __has_builtin(__builtin_tag_pointer_shift_or)
@@ -267,7 +267,7 @@ template <size_t Bits> struct bits_needed_tag {
// forward declaration
-template <typename _T, typename _Tag, typename _Schema> class tagged_ptr;
+template <typename _T, typename _Tag, typename _Schema = memory::alignment_low_bits_tag> class tagged_ptr;
struct already_tagged_t {
consteval explicit already_tagged_t(int) noexcept { }
@@ -318,7 +318,7 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
using clean_pointer = typename schema::clean_pointer;
static constexpr auto clean_pointer_free_bits = std::pointer_traits<clean_pointer>::unused_bits;
- static constexpr auto dirty_pointer_free_bits = clean_pointer_free_bits & schema::used_bits;
+ static constexpr auto dirty_pointer_free_bits = clean_pointer_free_bits & ~static_cast<uintptr_t>(schema::used_bits);
template <uintptr_t FreeBits> static constexpr bool has_all_bits_available = (schema::used_bits & FreeBits) == schema::used_bits;
using tag_type = typename schema::tag_type;
@@ -352,6 +352,13 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
// construct which allows to communicate overaligned pointer to push over-aligned pointer
template <size_t Alignment> explicit constexpr tagged_ptr(overaligned_pointer_t<Alignment>, clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits | (std::alignment_free_bits<Alignment>)>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
+#if __has_builtin(__builtin_is_aligned)
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__builtin_is_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignment for tagging");
+#else
+ if !consteval {
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(reinterpret_cast<uintptr_t>(_ptr) % Alignment == 0, "Pointer must be aligned by provided alignemt for tagging");
+ }
+#endif
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
@@ -539,7 +546,7 @@ template <typename _T, typename _Tag, typename _Schema>
struct _LIBCPP_TEMPLATE_VIS pointer_traits<tagged_ptr<_T, _Tag, _Schema>> {
using _tagged_ptr = tagged_ptr<_T, _Tag, _Schema>;
using pointer = _tagged_ptr::clean_pointer;
- using element_type = _tagged_ptr::value_type;
+ using element_type = _tagged_ptr::element_type;
using difference_type = _tagged_ptr::difference_type;
template <typename _Up> using rebind = typename _tagged_ptr::template rebind<_Up>;
diff --git a/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp b/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
index 1e0dae4cf0b5f9..3c7492fc37afea 100644
--- a/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
+++ b/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
@@ -75,7 +75,30 @@ HANA_TEST {
uintptr_t tag = 0b11000111u;
auto tptr = std::tagged_ptr<int64_t[3], uintptr_t, std::memory::low_byte_tag>(&a, tag);
+
+ auto * original = tptr.pointer();
+ HANA_ASSERT(tag == tptr.tag());
+ HANA_ASSERT(original == &a);
+
+ auto [p, t] = tptr;
+ HANA_ASSERT(p == &a);
+ HANA_ASSERT(t == tag);
+
+ HANA_ASSERT(tptr[0] == 1);
+ HANA_ASSERT(tptr[1] == 2);
+ HANA_ASSERT(tptr[2] == 3);
+
+
+ return true;
+};
+
+HANA_TEST {
+ int64_t a[3] = {1,2,3};
+ uintptr_t tag = 0b1111u;
+ // this only works on ARM64 as it needs available bits on left position
+ auto tptr = std::tagged_ptr<int64_t[3], uintptr_t, std::memory::left_shift_tag<4>>(&a, tag);
+
auto * original = tptr.pointer();
HANA_ASSERT(tag == tptr.tag());
HANA_ASSERT(original == &a);
@@ -146,13 +169,42 @@ HANA_TEST {
HANA_TEST {
int a{14};
int * b = &a;
- auto tptr = std::tagged_ptr<int, uintptr_t, std::memory::alignment_low_bits_tag>(b);
+ auto tptr = std::tagged_ptr<int, uintptr_t, std::memory::alignment_low_bits_tag>(b, 0b1u);
HANA_ASSERT(tptr.pointer() == &a);
return true;
};
+HANA_TEST {
+ alignas(16) int a{14};
+ int * b = &a;
+ auto tptr = std::tagged_ptr<int, uintptr_t, std::memory::custom_alignment_tag<16>>(std::overaligned_pointer<16>, b, 0b1u);
+ return true;
+};
+
+HANA_TEST {
+ int a{14};
+ int * b = &a;
+ auto tptr = std::tagged_ptr<int, uintptr_t, std::memory::bits_needed_tag<2>>(b, 0b11u);
+ return true;
+};
+HANA_TEST {
+ struct overaligned_int { alignas(16) int x; };
+
+ using first_ptraits = std::pointer_traits<overaligned_int *>;
+ static_assert((first_ptraits::unused_bits & 0b1111ull) == 0b1111ull);
+
+ auto v = overaligned_int{4};
+ auto tptr = std::tagged_ptr<overaligned_int, uintptr_t, std::memory::bitmask_tag<0b11>>(&v, 0b11u);
+ HANA_ASSERT(tptr.pointer() == &v);
+ HANA_ASSERT(tptr.tag() == 0b11u);
+
+ using ptraits = std::pointer_traits<decltype(tptr)>;
+
+ static_assert((ptraits::unused_bits & 0b1111ull) == 0b1100ull);
+ return true;
+};
return 0;
}
>From e3ae20111e280afaae8e58f34d0e32c71f41956c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Sat, 12 Oct 2024 00:08:05 +0200
Subject: [PATCH 6/9] add a lot of nodiscard
---
libcxx/include/__memory/tagged_ptr.h | 54 ++++++++++++++--------------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/libcxx/include/__memory/tagged_ptr.h b/libcxx/include/__memory/tagged_ptr.h
index c46c43f032b5dc..9140d26c1a5f90 100644
--- a/libcxx/include/__memory/tagged_ptr.h
+++ b/libcxx/include/__memory/tagged_ptr.h
@@ -343,15 +343,15 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
tagged_ptr & operator=(const tagged_ptr &) = default;
tagged_ptr & operator=(tagged_ptr &&) = default;
- explicit constexpr tagged_ptr(already_tagged_t, dirty_pointer _ptr) noexcept: _pointer{_ptr} { }
+ [[nodiscard]] constexpr tagged_ptr(already_tagged_t, dirty_pointer _ptr) noexcept: _pointer{_ptr} { }
- explicit constexpr tagged_ptr(clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
+ [[nodiscard]] constexpr tagged_ptr(clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
}
// construct which allows to communicate overaligned pointer to push over-aligned pointer
- template <size_t Alignment> explicit constexpr tagged_ptr(overaligned_pointer_t<Alignment>, clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits | (std::alignment_free_bits<Alignment>)>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
+ template <size_t Alignment> [[nodiscard]] constexpr tagged_ptr(overaligned_pointer_t<Alignment>, clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits | (std::alignment_free_bits<Alignment>)>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
#if __has_builtin(__builtin_is_aligned)
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__builtin_is_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignment for tagging");
#else
@@ -365,14 +365,14 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
}
// construct which allows manually provide known free bits (platform specific)
- template <uintptr_t AdditionalFreeBits> explicit constexpr tagged_ptr(known_unused_bits_t<AdditionalFreeBits>, clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits | AdditionalFreeBits>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
+ template <uintptr_t AdditionalFreeBits> [[nodiscard]] constexpr tagged_ptr(known_unused_bits_t<AdditionalFreeBits>, clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits | AdditionalFreeBits>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
}
// accessors
- constexpr decltype(auto) operator*() const noexcept {
+ [[nodiscard]] constexpr decltype(auto) operator*() const noexcept {
return *pointer();
}
@@ -380,11 +380,11 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
return pointer();
}
- template <typename...Ts> constexpr decltype(auto) operator[](Ts... args) const noexcept requires std::is_array_v<element_type> && (sizeof...(Ts) == std::rank_v<element_type>) {
+ template <typename...Ts> [[nodiscard]] constexpr decltype(auto) operator[](Ts... args) const noexcept requires std::is_array_v<element_type> && (sizeof...(Ts) == std::rank_v<element_type>) {
return (*pointer())[args...];
}
- constexpr decltype(auto) operator[](difference_type diff) const noexcept requires (!std::is_array_v<element_type>) {
+ [[nodiscard]] constexpr decltype(auto) operator[](difference_type diff) const noexcept requires (!std::is_array_v<element_type>) {
return *(pointer() + diff);
}
@@ -407,7 +407,7 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
return *this;
}
- constexpr auto operator++(int) noexcept {
+ [[nodiscard]] constexpr auto operator++(int) noexcept {
auto copy = auto(*this);
this->operator++();
return copy;
@@ -418,22 +418,22 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
return *this;
}
- friend constexpr auto operator+(tagged_ptr lhs, difference_type diff) noexcept {
+ [[nodiscard]] friend constexpr auto operator+(tagged_ptr lhs, difference_type diff) noexcept {
lhs += diff;
return lhs;
}
- friend constexpr auto operator+(difference_type diff, tagged_ptr rhs) noexcept {
+ [[nodiscard]] friend constexpr auto operator+(difference_type diff, tagged_ptr rhs) noexcept {
rhs += diff;
return rhs;
}
- friend constexpr auto operator-(tagged_ptr lhs, difference_type diff) noexcept {
+ [[nodiscard]] friend constexpr auto operator-(tagged_ptr lhs, difference_type diff) noexcept {
lhs -= diff;
return lhs;
}
- friend constexpr auto operator-(difference_type diff, tagged_ptr rhs) noexcept {
+ [[nodiscard]] friend constexpr auto operator-(difference_type diff, tagged_ptr rhs) noexcept {
rhs -= diff;
return rhs;
}
@@ -448,23 +448,23 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
return *this;
}
- constexpr auto operator--(int) noexcept {
+ [[nodiscard]] constexpr auto operator--(int) noexcept {
auto copy = auto(*this);
this->operator--();
return copy;
}
// observers
- constexpr dirty_pointer unsafe_dirty_pointer() const noexcept {
+ [[nodiscard]] constexpr dirty_pointer unsafe_dirty_pointer() const noexcept {
// this function is not intentionally constexpr, as it is needed only to interact with
// existing runtime code
return _pointer;
}
- static constexpr bool support_aliasing_masking = pointer_tagging_schema_with_aliasing<schema>;
+ static constexpr bool _support_aliasing_masking = pointer_tagging_schema_with_aliasing<schema>;
- constexpr clean_pointer aliasing_pointer() const noexcept {
- if constexpr (support_aliasing_masking) {
+ [[nodiscard]] constexpr clean_pointer aliasing_pointer() const noexcept {
+ if constexpr (_support_aliasing_masking) {
if !consteval {
return schema::recover_aliasing_pointer(_pointer);
}
@@ -473,15 +473,15 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
return schema::recover_pointer(_pointer);
}
- constexpr clean_pointer pointer() const noexcept {
+ [[nodiscard]] constexpr clean_pointer pointer() const noexcept {
return schema::recover_pointer(_pointer);
}
- constexpr tag_type tag() const noexcept {
+ [[nodiscard]] constexpr tag_type tag() const noexcept {
return schema::recover_tag(_pointer);
}
- template <std::size_t I> [[nodiscard, clang::always_inline]] friend constexpr decltype(auto) get(tagged_ptr _pair) noexcept {
+ template <std::size_t I> [[nodiscard]] friend constexpr decltype(auto) get(tagged_ptr _pair) noexcept {
static_assert(I < 3);
if constexpr (I == 0) {
return _pair.pointer();
@@ -490,16 +490,16 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
}
}
- constexpr explicit operator bool() const noexcept {
+ [[nodiscard]] constexpr explicit operator bool() const noexcept {
return pointer() != nullptr;
}
- friend constexpr ptrdiff_t operator-(tagged_ptr lhs, tagged_ptr rhs) noexcept {
+ [[nodiscard]] friend constexpr ptrdiff_t operator-(tagged_ptr lhs, tagged_ptr rhs) noexcept {
return lhs.pointer() - rhs.pointer();
}
// comparison operators
- friend bool operator==(tagged_ptr, tagged_ptr) = default;
+ [[nodiscard]] friend bool operator==(tagged_ptr, tagged_ptr) = default;
struct _compare_object {
clean_pointer pointer;
@@ -508,16 +508,16 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
friend auto operator<=>(_compare_object, _compare_object) = default;
};
- friend constexpr auto operator<=>(tagged_ptr lhs, tagged_ptr rhs) noexcept {
+ [[nodiscard]] friend constexpr auto operator<=>(tagged_ptr lhs, tagged_ptr rhs) noexcept {
return _compare_object{lhs.pointer(), lhs.tag()} <=> _compare_object{rhs.pointer(), rhs.tag()};
}
- friend constexpr bool operator==(tagged_ptr lhs, clean_pointer rhs) noexcept {
+ [[nodiscard]] friend constexpr bool operator==(tagged_ptr lhs, clean_pointer rhs) noexcept {
return lhs.pointer() == rhs;
}
- friend constexpr auto operator<=>(tagged_ptr lhs, clean_pointer rhs) noexcept {
+ [[nodiscard]] friend constexpr auto operator<=>(tagged_ptr lhs, clean_pointer rhs) noexcept {
return lhs.pointer() <=> rhs;
}
- friend constexpr bool operator==(tagged_ptr lhs, nullptr_t) noexcept {
+ [[nodiscard]] friend constexpr bool operator==(tagged_ptr lhs, nullptr_t) noexcept {
return lhs.pointer() == nullptr;
}
};
>From bed3d185766576d4eaed95c9ac6b87964c5c0cea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Sat, 12 Oct 2024 00:21:32 +0200
Subject: [PATCH 7/9] add __is_ptr_aligned
---
libcxx/include/__memory/tagged_ptr.h | 26 ++++++++++----------------
1 file changed, 10 insertions(+), 16 deletions(-)
diff --git a/libcxx/include/__memory/tagged_ptr.h b/libcxx/include/__memory/tagged_ptr.h
index 9140d26c1a5f90..d43a6affdeda19 100644
--- a/libcxx/include/__memory/tagged_ptr.h
+++ b/libcxx/include/__memory/tagged_ptr.h
@@ -48,6 +48,14 @@ template <typename T> concept pointer_tagging_schema_with_aliasing = pointer_tag
template <size_t Alignment> requires (std::has_single_bit(Alignment)) constexpr uintptr_t alignment_free_bits = static_cast<uintptr_t>(Alignment) - 1ull;
+constexpr bool __is_ptr_aligned(const void * _ptr, size_t _alignment) noexcept {
+#if __has_builtin(__builtin_is_aligned)
+ return __builtin_is_aligned(_ptr, _alignment);
+#else
+ return reinterpret_cast<uintptr_t>(_ptr) % _alignment == 0;
+#endif
+}
+
namespace memory {
// no-op schema so I can better explain how schemas work
@@ -127,13 +135,7 @@ template <unsigned Alignment> struct custom_alignment_tag {
static constexpr auto used_bits = _underlying_schema::used_bits;
static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
-#if __has_builtin(__builtin_is_aligned)
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__builtin_is_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignment for tagging");
-#else
- if !consteval {
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(reinterpret_cast<uintptr_t>(_ptr) % Alignment == 0, "Pointer must be aligned by provided alignemt for tagging");
- }
-#endif
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::__is_ptr_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignment for tagging");
return _underlying_schema::encode_pointer_with_tag(_ptr, _value);
}
@@ -265,7 +267,6 @@ template <size_t Bits> struct bits_needed_tag {
}
-
// forward declaration
template <typename _T, typename _Tag, typename _Schema = memory::alignment_low_bits_tag> class tagged_ptr;
@@ -352,14 +353,7 @@ template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
// construct which allows to communicate overaligned pointer to push over-aligned pointer
template <size_t Alignment> [[nodiscard]] constexpr tagged_ptr(overaligned_pointer_t<Alignment>, clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits | (std::alignment_free_bits<Alignment>)>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
-#if __has_builtin(__builtin_is_aligned)
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__builtin_is_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignment for tagging");
-#else
- if !consteval {
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(reinterpret_cast<uintptr_t>(_ptr) % Alignment == 0, "Pointer must be aligned by provided alignemt for tagging");
- }
-#endif
-
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::__is_ptr_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignment for tagging");
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
_LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
}
>From 6aeb3788d691a8e116fa7877101d32903b6e4da3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Sun, 12 Jan 2025 21:34:53 +0100
Subject: [PATCH 8/9] Simplified design
---
libcxx/include/CMakeLists.txt | 2 +-
libcxx/include/__memory/pointer_tag_pair.h | 221 +++++++
libcxx/include/__memory/tagged_ptr.h | 571 ------------------
libcxx/include/memory | 2 +-
.../memory/tagged_ptr/tagged_ptr.pass.cpp | 211 -------
.../tagged_ptr/tagged_ptr.pass.cpp.exec | Bin 16856 -> 0 bytes
6 files changed, 223 insertions(+), 784 deletions(-)
create mode 100644 libcxx/include/__memory/pointer_tag_pair.h
delete mode 100644 libcxx/include/__memory/tagged_ptr.h
delete mode 100644 libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
delete mode 100755 libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp.exec
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 7be458708c8101..a3a97a551a124d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -538,12 +538,12 @@ set(files
__memory/inout_ptr.h
__memory/out_ptr.h
__memory/pointer_traits.h
+ __memory/pointer_tag_pair.h
__memory/ranges_construct_at.h
__memory/ranges_uninitialized_algorithms.h
__memory/raw_storage_iterator.h
__memory/shared_ptr.h
__memory/swap_allocator.h
- __memory/tagged_ptr.h
__memory/temp_value.h
__memory/temporary_buffer.h
__memory/uninitialized_algorithms.h
diff --git a/libcxx/include/__memory/pointer_tag_pair.h b/libcxx/include/__memory/pointer_tag_pair.h
new file mode 100644
index 00000000000000..14e8e9773063a2
--- /dev/null
+++ b/libcxx/include/__memory/pointer_tag_pair.h
@@ -0,0 +1,221 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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___POINTER_TAG_PAIR_H
+#define _LIBCPP___POINTER_TAG_PAIR_H
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26
+
+#include <__assert>
+#include <__bit/bit_width.h>
+#include <__bit/countr.h>
+#include <__config>
+#include <__tuple/tuple_element.h>
+#include <__tuple/tuple_size.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/is_enum.h>
+#include <__type_traits/is_integral.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/make_unsigned.h>
+#include <__type_traits/remove_cvref.h>
+#include <__type_traits/underlying_type.h>
+#include <__utility/swap.h>
+#include <climits>
+#include <compare>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+constexpr bool __is_ptr_aligned(const void* _ptr, size_t _alignment) noexcept {
+# if __has_builtin(__builtin_is_aligned)
+ return __builtin_is_aligned(_ptr, _alignment);
+# else
+ return reinterpret_cast<uintptr_t>(_ptr) % _alignment == 0;
+# endif
+}
+
+template <typename T>
+struct __tag_underlying_type {
+ using type = T;
+};
+
+template <typename T>
+ requires(std::is_enum_v<T>)
+struct __tag_underlying_type<T> {
+ using type = std::underlying_type_t<T>;
+};
+
+template <typename T>
+constexpr unsigned _alignment_bits_available = std::countr_zero(alignof(T));
+
+template <typename T, unsigned Bits> struct _few_bits {
+ T value : Bits;
+};
+
+template <typename T> struct _few_bits<T, 0> {
+ struct zero {
+ constexpr zero(T) noexcept { }
+ constexpr operator T() const noexcept {
+ return T{};
+ }
+ } value;
+};
+
+template <typename PointeeT, typename TagT, unsigned Bits = _alignment_bits_available<PointeeT>>
+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>>)
+struct pointer_tag_pair {
+public:
+ using element_type = PointeeT;
+ using pointer_type = PointeeT*;
+ using tagged_pointer_type = void *; // or uintptr_t?
+ using tag_type = TagT;
+
+private:
+ static constexpr unsigned _alignment_needed = (1u << Bits);
+ static constexpr uintptr_t _tag_mask = (_alignment_needed - 1u);
+ static constexpr uintptr_t _pointer_mask = ~_tag_mask;
+
+ using _underlaying_tag_type = typename __tag_underlying_type<tag_type>::type;
+ // using unsigned_tag_type = std::make_unsigned_t<_underlaying_tag_type>;
+ using unspecified_pointer_type = pointer_type;
+ using _real_tag_type = _few_bits<_underlaying_tag_type, Bits>;
+
+ unspecified_pointer_type _pointer{nullptr}; // required to have size same as sizeof(Pointee *)
+
+ static_assert(Bits < sizeof(pointer_type) * CHAR_BIT);
+
+ struct _tagged_t {};
+
+ constexpr pointer_tag_pair(_tagged_t, unspecified_pointer_type _ptr) noexcept : _pointer{_ptr} {}
+
+public:
+ pointer_tag_pair() = default; // always noexcept
+ constexpr pointer_tag_pair(nullptr_t) noexcept;
+ pointer_tag_pair(const pointer_tag_pair&) = default;
+ pointer_tag_pair(pointer_tag_pair&&) = default;
+ pointer_tag_pair& operator=(const pointer_tag_pair&) = default;
+ pointer_tag_pair& operator=(pointer_tag_pair&&) = default;
+
+ // to store and tag pointer (only if eligible)
+ // Precondition: bit_width(static_cast<make_usigned_t<U>>(tag)) <= Bits is true, where U is
+ // underlying_type_t<TagType>> if is_enum_v<TagType> and TagType otherwise. Precondition: alignof(ptr) (there is
+ // enough of bits) Mandates: alignment of element type >= bits requested This turn them into unsigned.
+ constexpr pointer_tag_pair(pointer_type _ptr, tag_type _tag)
+ requires(alignof(element_type) >= _alignment_needed)
+ {
+ const auto _native_tag = _real_tag_type{static_cast<_underlaying_tag_type>(_tag)}.value;
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
+ _native_tag == (static_cast<_underlaying_tag_type>(_tag)), "Tag value must fit into requested bits");
+ //_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::bit_width(_native_tag) <= Bits, "Tag type value must fits bits
+ //requested");
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
+ std::__is_ptr_aligned(_ptr, _alignment_needed), "Pointer must be aligned by provided alignment for tagging");
+
+ void* _tagged_ptr = __builtin_tag_pointer_mask_or((void*)_ptr, _native_tag, _tag_mask);
+ _pointer = static_cast<unspecified_pointer_type>(_tagged_ptr);
+ }
+
+ // Preconditions: p points to an object X of a type similar ([conv.qual]) to element_type, where X has alignment
+ // byte_alignment (inspired by aligned_accessor)
+ // The precondition needa to say null pointer value or the thing about pointing to object with aligment. ??
+ template <unsigned _AlignmentPromised>
+ static constexpr pointer_tag_pair from_overaligned(pointer_type _ptr, tag_type _tag) {
+ const auto _native_tag = _real_tag_type{static_cast<_underlaying_tag_type>(_tag)}.value;
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
+ _native_tag == (static_cast<_underlaying_tag_type>(_tag)), "Tag value must fit into requested bits");
+ //_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::bit_width(static_cast<unsigned_tag_type>(_tag)) <= Bits, "Tag type
+ //value must fits bits requested");
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
+ std::__is_ptr_aligned(_ptr, _AlignmentPromised), "Pointer must be aligned by provided alignment for tagging");
+
+ void* _tagged_ptr = __builtin_tag_pointer_mask_or((void*)_ptr, _native_tag, _tag_mask);
+ return pointer_tag_pair{_tagged_t{}, static_cast<unspecified_pointer_type>(_tagged_ptr)};
+ }
+
+ pointer_tag_pair from_tagged(tagged_pointer_type ptr) { // no-constexpr
+ // Precondition: valid pointer if untagged??
+ return pointer_tag_pair{_tagged_t{}, reinterpret_cast<unspecified_pointer_type>(ptr)};
+ }
+
+ // destructor
+ ~pointer_tag_pair() = default;
+
+ // accessors
+ tagged_pointer_type tagged_pointer() const noexcept {
+ return reinterpret_cast<tagged_pointer_type>(_pointer);
+ }
+ constexpr pointer_type pointer() const noexcept {
+ return static_cast<pointer_type>(__builtin_tag_pointer_mask((void*)_pointer, _pointer_mask));
+ }
+ constexpr tag_type tag() const noexcept {
+ const uintptr_t r = __builtin_tag_pointer_mask_as_int((void*)_pointer, _tag_mask);
+ return static_cast<tag_type>(_real_tag_type{.value = static_cast<_underlaying_tag_type>(r)}.value);
+ }
+
+ // swap
+ constexpr void swap(pointer_tag_pair& _rhs) noexcept { std::swap(_pointer, _rhs._pointer); }
+
+ // comparing {pointer(), tag()} <=> {pointer(), tag()} for consistency
+ friend constexpr auto operator<=>(pointer_tag_pair lhs, pointer_tag_pair rhs) noexcept {
+ const auto _ptr_comp = lhs.pointer() <=> rhs.pointer();
+ if (!std::is_eq(_ptr_comp)) {
+ return _ptr_comp;
+ }
+
+ return lhs.tag() <=> rhs.tag();
+ }
+ friend bool operator==(pointer_tag_pair, pointer_tag_pair) = default;
+};
+
+// support for structured bindings
+template <typename _Pointee, typename _Tag, unsigned _Bits>
+struct tuple_size<pointer_tag_pair<_Pointee, _Tag, _Bits>> : std::integral_constant<std::size_t, 2> {};
+
+template <typename _Pointee, typename _Tag, unsigned _Bits>
+struct tuple_element<0, pointer_tag_pair<_Pointee, _Tag, _Bits>> {
+ using type = _Pointee*;
+};
+
+template <typename _Pointee, typename _Tag, unsigned _Bits>
+struct tuple_element<1, pointer_tag_pair<_Pointee, _Tag, _Bits>> {
+ using type = _Tag;
+};
+
+template <typename _Pointee, typename _Tag, unsigned _Bits>
+struct tuple_element<0, const pointer_tag_pair<_Pointee, _Tag, _Bits>> {
+ using type = _Pointee*;
+};
+
+template <typename _Pointee, typename _Tag, unsigned _Bits>
+struct tuple_element<1, const pointer_tag_pair<_Pointee, _Tag, _Bits>> {
+ using type = _Tag;
+};
+
+// helpers
+
+// std::get (with one overload as copying pointer_tag_pair is cheap)
+template <size_t _I, typename _Pointee, typename _Tag, unsigned _Bits>
+constexpr auto get(pointer_tag_pair<_Pointee, _Tag, _Bits> _pair) noexcept
+ -> tuple_element<_I, pointer_tag_pair<_Pointee, _Tag, _Bits>>::type {
+ if constexpr (_I == 0) {
+ return _pair.pointer();
+ } else {
+ static_assert(_I == 1);
+ return _pair.tag();
+ }
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 26
+
+#endif // _LIBCPP___POINTER_TAG_PAIR_H
\ No newline at end of file
diff --git a/libcxx/include/__memory/tagged_ptr.h b/libcxx/include/__memory/tagged_ptr.h
deleted file mode 100644
index d43a6affdeda19..00000000000000
--- a/libcxx/include/__memory/tagged_ptr.h
+++ /dev/null
@@ -1,571 +0,0 @@
-// -*- C++ -*-
-//===----------------------------------------------------------------------===//
-//
-// 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___TAGGED_PTR_H
-#define _LIBCPP___TAGGED_PTR_H
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-# pragma GCC system_header
-#endif
-
-#if _LIBCPP_STD_VER >= 26
-
-#include <__config>
-#include <__type_traits/is_trivially_copyable.h>
-#include <__assert>
-#include <__bit/has_single_bit.h>
-#include <__type_traits/rank.h>
-#include <__memory/pointer_traits.h>
-#include <compare>
-
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-template <typename T, typename Y> concept convertible_to_from = std::convertible_to<Y, T> && std::convertible_to<T, Y>;
-
-template <typename T> concept pointer_tagging_schema =
-requires {
- typename T::clean_pointer;
- typename T::dirty_pointer;
- typename T::tag_type;
-} && requires(T::dirty_pointer payload, T::clean_pointer clean, T::tag_type tag) {
- requires convertible_to_from<typename T::tag_type, uintptr_t>;
- requires std::is_pointer_v<typename T::clean_pointer>;
-
- { T::encode_pointer_with_tag(clean, tag) } noexcept -> std::same_as<typename T::dirty_pointer>;
- { T::recover_pointer(payload) } noexcept -> std::same_as<typename T::clean_pointer>;
- { T::recover_tag(payload) } noexcept -> std::same_as<typename T::tag_type>;
-};
-
-template <typename T> concept pointer_tagging_schema_with_aliasing = pointer_tagging_schema<T> && requires(T::dirty_pointer payload) {
- { T::recover_aliasing_pointer(payload) } noexcept -> std::same_as<typename T::clean_pointer>;
-};
-
-template <size_t Alignment> requires (std::has_single_bit(Alignment)) constexpr uintptr_t alignment_free_bits = static_cast<uintptr_t>(Alignment) - 1ull;
-
-constexpr bool __is_ptr_aligned(const void * _ptr, size_t _alignment) noexcept {
-#if __has_builtin(__builtin_is_aligned)
- return __builtin_is_aligned(_ptr, _alignment);
-#else
- return reinterpret_cast<uintptr_t>(_ptr) % _alignment == 0;
-#endif
-}
-
-namespace memory {
-
-// no-op schema so I can better explain how schemas work
-struct no_tag {
- template <typename T, typename Tag> struct schema {
- using clean_pointer = T *;
- using dirty_pointer = void *;
- using tag_type = Tag;
-
- static constexpr uintptr_t _mask = 0u;
- static constexpr auto used_bits = _mask;
-
- static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type) noexcept {
- return (dirty_pointer)_ptr;
- }
- static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
- return (clean_pointer)_ptr;
- }
- static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
- return (clean_pointer)_ptr;
- }
- static constexpr tag_type recover_tag(dirty_pointer) noexcept {
- return {};
- }
- };
-};
-
-// most basic schema for tagging
-// it lets user to provide their own mask
-template <uintptr_t Mask> struct bitmask_tag {
- template <typename T, typename Tag> struct schema {
- using clean_pointer = T *;
- using dirty_pointer = void *;
- using tag_type = Tag;
-
- static constexpr uintptr_t _mask = Mask;
-
- static constexpr auto used_bits = _mask;
-
- static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
-#if __has_builtin(__builtin_tag_pointer_mask_or)
- return static_cast<dirty_pointer>(__builtin_tag_pointer_mask_or((void *)(_ptr), static_cast<uintptr_t>(_value), _mask));
-#else
- return reinterpret_cast<dirty_pointer>((reinterpret_cast<uintptr_t>(_ptr) & static_cast<uintptr_t>(_mask)) | (static_cast<uintptr_t>(_value) & ~static_cast<uintptr_t>(_mask)));
-#endif
- }
- static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
-#if __has_builtin(__builtin_tag_pointer_mask)
- return static_cast<clean_pointer>(__builtin_tag_pointer_mask((void *)_ptr, ~_mask));
-#else
- return reinterpret_cast<clean_pointer>(reinterpret_cast<uintptr_t>(_ptr) & ~static_cast<uintptr_t>(_mask));
-#endif
- }
- static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
-#if __has_builtin(__builtin_tag_pointer_mask_as_int)
- return static_cast<tag_type>(__builtin_tag_pointer_mask_as_int((void *)_ptr, _mask));
-#else
- return static_cast<tag_type>(reinterpret_cast<uintptr_t>(_ptr) & static_cast<uintptr_t>(_mask));
-#endif
- }
- };
-};
-
-// schema which allows only pointer of custom provided minimal alignment
-// otherwise it behaves as custom mask schema
-template <unsigned Alignment> struct custom_alignment_tag {
- static_assert(std::has_single_bit(Alignment), "alignment must be power of 2");
- static constexpr uintptr_t mask = alignment_free_bits<Alignment>;
-
- template <typename T, typename Tag> struct schema: bitmask_tag<mask>::template schema<T, Tag> {
- using _underlying_schema = bitmask_tag<mask>::template schema<T, Tag>;
-
- using clean_pointer = _underlying_schema::clean_pointer;
- using dirty_pointer = _underlying_schema::dirty_pointer;
- using tag_type = _underlying_schema::tag_type;
-
- static constexpr auto used_bits = _underlying_schema::used_bits;
-
- static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::__is_ptr_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignment for tagging");
- return _underlying_schema::encode_pointer_with_tag(_ptr, _value);
- }
-
- using _underlying_schema::recover_pointer;
- using _underlying_schema::recover_tag;
- };
-};
-
-// default scheme which gives only bits from alignment
-struct alignment_low_bits_tag {
- template <typename T, typename Tag> using schema = typename custom_alignment_tag<alignof(T)>::template schema<T, Tag>;
-};
-
-// scheme which shifts bits to left by Bits bits and gives the space for tagging
-template <unsigned Bits> struct left_shift_tag {
- static constexpr unsigned _shift = Bits;
- static constexpr uintptr_t _mask = (uintptr_t{1u} << _shift) - 1u;
-
- template <typename T, typename Tag> struct schema {
- using clean_pointer = T *;
- using dirty_pointer = void *;
- using tag_type = Tag;
-
- // what to do here?
- static constexpr uintptr_t used_bits = ~((~uintptr_t{0}) >> _shift);
-
- static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
-#if __has_builtin(__builtin_tag_pointer_shift_or)
- return static_cast<dirty_pointer>(__builtin_tag_pointer_shift_or((void *)(_ptr), (uintptr_t)_value, _shift));
-#else
- return reinterpret_cast<dirty_pointer>((reinterpret_cast<uintptr_t>(_ptr) << _shift) | (static_cast<uintptr_t>(_value) & ((1ull << static_cast<uintptr_t>(_shift)) - 1ull)));
-#endif
- }
- static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
-#if __has_builtin(__builtin_tag_pointer_unshift)
- return static_cast<clean_pointer>(__builtin_tag_pointer_unshift((void *)_ptr, _shift));
-#else
- return reinterpret_cast<clean_pointer>(reinterpret_cast<uintptr_t>(_ptr) >> _shift);
-#endif
- }
- static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
-#if __has_builtin(__builtin_tag_pointer_mask_as_int)
- return static_cast<tag_type>(__builtin_tag_pointer_mask_as_int((void *)_ptr, _mask));
-#else
- return static_cast<tag_type>(reinterpret_cast<uintptr_t>(_ptr) & static_cast<uintptr_t>(_mask));
-#endif
- }
- };
-};
-
-// scheme which shifts pointer to left by 8 bits and give this space as guaranteed space for tagging
-struct low_byte_tag {
- template <typename T, typename Tag> using schema = typename left_shift_tag<8>::template schema<T, Tag>;
-};
-
-// this will give user access to upper byte of pointer on aarch64
-// also it supports recovering aliasing pointer as no-op (fast-path)
-struct upper_byte_tag {
- template <typename T> static constexpr unsigned _shift = sizeof(T *) * 8ull - 8ull;
- template <typename T> static constexpr uintptr_t _mask = 0b1111'1111ull << _shift<T>;
-
- template <typename T, typename Tag> struct schema: bitmask_tag<_mask<T>>::template schema<T, Tag> {
- using _underlying_schema = bitmask_tag<_mask<T>>::template schema<T, Tag>;
-
- using clean_pointer = _underlying_schema::clean_pointer;
- using dirty_pointer = _underlying_schema::dirty_pointer;
- using tag_type = _underlying_schema::tag_type;
-
- static constexpr uintptr_t used_bits = _mask<T>;
-
- static constexpr clean_pointer recover_aliasing_pointer(dirty_pointer _ptr) noexcept {
- return (clean_pointer)_ptr;
- }
-
- using _underlying_schema::encode_pointer_with_tag;
- using _underlying_schema::recover_pointer;
- using _underlying_schema::recover_tag;
- };
-};
-
-// improved version of previous aarch64 upper byte scheme
-// with added shifting tag value into position, so the tag doesn't need to know about exact position
-struct upper_byte_shifted_tag: upper_byte_tag {
- template <typename T, typename Tag> struct schema: upper_byte_tag::template schema<T, uintptr_t> {
- using _underlying_schema = upper_byte_tag::template schema<T, uintptr_t>;
-
- using clean_pointer = _underlying_schema::clean_pointer;
- using dirty_pointer = _underlying_schema::dirty_pointer;
- using tag_type = Tag;
-
- static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
- return _underlying_schema::encode_pointer_with_tag(_ptr, static_cast<uintptr_t>(_value) << upper_byte_tag::_shift<T>);
- }
- static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
- return static_cast<tag_type>(_underlying_schema::recover_tag(_ptr) >> upper_byte_tag::_shift<T>);
- }
-
- using _underlying_schema::recover_pointer;
- using _underlying_schema::recover_aliasing_pointer;
- };
-};
-
-// "clever" schema which can use any bits
-template <size_t Bits> struct bits_needed_tag {
- template <typename _T, typename _Tag> struct schema {
- static constexpr auto _available_bits = std::pointer_traits<_T *>::unused_bits;
- static constexpr auto _mask = (uintptr_t{1} << Bits) - uintptr_t{1}; /* FIXME: use lowest N bits from _available_bits */
-
- using _underlying_schema = bitmask_tag<_mask>::template schema<_T, uintptr_t>;
-
- static constexpr auto used_bits = _mask;
-
- using clean_pointer = _underlying_schema::clean_pointer;
- using dirty_pointer = _underlying_schema::dirty_pointer;
- using tag_type = _Tag;
-
- static constexpr dirty_pointer encode_pointer_with_tag(clean_pointer _ptr, tag_type _value) noexcept {
- // TODO something clever :)
- return _underlying_schema::encode_pointer_with_tag(_ptr, _value);
- }
- static constexpr clean_pointer recover_pointer(dirty_pointer _ptr) noexcept {
- return _underlying_schema::recover_pointer(_ptr);
- }
- static constexpr tag_type recover_tag(dirty_pointer _ptr) noexcept {
- return static_cast<tag_type>(_underlying_schema::recover_tag(_ptr));
- }
- };
-};
-
-}
-
-// forward declaration
-template <typename _T, typename _Tag, typename _Schema = memory::alignment_low_bits_tag> class tagged_ptr;
-
-struct already_tagged_t {
- consteval explicit already_tagged_t(int) noexcept { }
-};
-
-template <size_t Alignment> requires (std::has_single_bit(Alignment)) struct overaligned_pointer_t {
- consteval explicit overaligned_pointer_t(int) noexcept { }
-};
-
-template <uintptr_t FreeBits> struct known_unused_bits_t {
- consteval explicit known_unused_bits_t(int) noexcept { }
-};
-
-constexpr auto already_tagged = already_tagged_t{0};
-
-template <size_t Alignment> requires (std::has_single_bit(Alignment)) constexpr auto overaligned_pointer = overaligned_pointer_t<Alignment>{0};
-
-template <uintptr_t FreeBits> auto known_unused_bits = known_unused_bits_t<FreeBits>{0};
-
-template <typename _Schema2, typename _T, typename _Tag, typename _Schema> constexpr auto scheme_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
- return tagged_ptr<_T, _Tag, _Schema2>{in.pointer(), in.tag()};
-}
-
-template <typename _Y, typename _T, typename _Tag, typename _Schema> constexpr auto const_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
- // TODO we can just use native pointer here
- return tagged_ptr<_Y, _Tag, _Schema>{const_cast<_Y*>(in.pointer()), in.tag()};
-}
-
-template <typename _Y, typename _T, typename _Tag, typename _Schema> constexpr auto static_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
- return tagged_ptr<_Y, _Tag, _Schema>{static_cast<_Y*>(in.pointer()), in.tag()};
-}
-
-template <typename _Y, typename _T, typename _Tag, typename _Schema> constexpr auto dynamic_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
- return tagged_ptr<_Y, _Tag, _Schema>{dynamic_cast<_Y*>(in.pointer()), in.tag()};
-}
-
-template <typename _Y, typename _T, typename _Tag, typename _Schema> auto reinterpret_pointer_cast(tagged_ptr<_T, _Tag, _Schema> in) noexcept {
- return tagged_ptr<_Y, _Tag, _Schema>{reinterpret_cast<_Y*>(in.pointer()), in.tag()};
-}
-
-
-// wrapper class containing the pointer value and provides access
-template <typename _T, typename _Tag, typename _Schema> class tagged_ptr {
-
-public:
- using schema = typename _Schema::template schema<_T, _Tag>;
- using dirty_pointer = typename schema::dirty_pointer;
- using clean_pointer = typename schema::clean_pointer;
-
- static constexpr auto clean_pointer_free_bits = std::pointer_traits<clean_pointer>::unused_bits;
- static constexpr auto dirty_pointer_free_bits = clean_pointer_free_bits & ~static_cast<uintptr_t>(schema::used_bits);
- template <uintptr_t FreeBits> static constexpr bool has_all_bits_available = (schema::used_bits & FreeBits) == schema::used_bits;
-
- using tag_type = typename schema::tag_type;
-
- using element_type = typename std::pointer_traits<clean_pointer>::element_type;
- using difference_type = typename std::pointer_traits<clean_pointer>::difference_type;
-
- template <typename _Y> using rebind = tagged_ptr<_Y, _Tag, _Schema>;
-
-private:
-
- dirty_pointer _pointer{nullptr};
-
- template <typename _Y, typename _T2, typename _Tag2, typename _Schema2> constexpr auto const_pointer_cast(tagged_ptr<_T2, _Tag2, _Schema2> in) noexcept -> rebind<_T>;
-
-public:
- tagged_ptr() = default;
- constexpr tagged_ptr(nullptr_t) noexcept: _pointer{nullptr} { }
- tagged_ptr(const tagged_ptr &) = default;
- tagged_ptr(tagged_ptr &&) = default;
- ~tagged_ptr() = default;
- tagged_ptr & operator=(const tagged_ptr &) = default;
- tagged_ptr & operator=(tagged_ptr &&) = default;
-
- [[nodiscard]] constexpr tagged_ptr(already_tagged_t, dirty_pointer _ptr) noexcept: _pointer{_ptr} { }
-
- [[nodiscard]] constexpr tagged_ptr(clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
- _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
- _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
- }
-
- // construct which allows to communicate overaligned pointer to push over-aligned pointer
- template <size_t Alignment> [[nodiscard]] constexpr tagged_ptr(overaligned_pointer_t<Alignment>, clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits | (std::alignment_free_bits<Alignment>)>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::__is_ptr_aligned(_ptr, Alignment), "Pointer must be aligned by provided alignment for tagging");
- _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
- _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
- }
-
- // construct which allows manually provide known free bits (platform specific)
- template <uintptr_t AdditionalFreeBits> [[nodiscard]] constexpr tagged_ptr(known_unused_bits_t<AdditionalFreeBits>, clean_pointer _ptr, tag_type _tag) noexcept requires (has_all_bits_available<std::pointer_traits<clean_pointer>::unused_bits | AdditionalFreeBits>): _pointer{schema::encode_pointer_with_tag(_ptr, _tag)} {
- _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(pointer() == _ptr, "pointer must be recoverable after untagging");
- _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(tag() == _tag, "stored tag must be recoverable and within schema provided bit capacity");
- }
-
-
- // accessors
- [[nodiscard]] constexpr decltype(auto) operator*() const noexcept {
- return *pointer();
- }
-
- constexpr clean_pointer operator->() const noexcept {
- return pointer();
- }
-
- template <typename...Ts> [[nodiscard]] constexpr decltype(auto) operator[](Ts... args) const noexcept requires std::is_array_v<element_type> && (sizeof...(Ts) == std::rank_v<element_type>) {
- return (*pointer())[args...];
- }
-
- [[nodiscard]] constexpr decltype(auto) operator[](difference_type diff) const noexcept requires (!std::is_array_v<element_type>) {
- return *(pointer() + diff);
- }
-
- // swap
- friend constexpr void swap(tagged_ptr & lhs, tagged_ptr & rhs) noexcept {
- std::swap(lhs._pointer, rhs._pointer);
- }
-
- // modifiers for tag
- constexpr auto & set(tag_type new_tag) noexcept {
- // this is here so I can avoid checks
- // TODO we should be able to check what bits available
- _pointer = schema::encode_pointer_with_tag(pointer(), new_tag);
- return *this;
- }
-
- // modifiers for pointer
- constexpr auto & operator++() noexcept {
- _pointer = tagged_ptr{pointer()+1u, tag()}._pointer;
- return *this;
- }
-
- [[nodiscard]] constexpr auto operator++(int) noexcept {
- auto copy = auto(*this);
- this->operator++();
- return copy;
- }
-
- constexpr auto & operator+=(difference_type diff) noexcept {
- _pointer = tagged_ptr{pointer()+diff, tag()}._pointer;
- return *this;
- }
-
- [[nodiscard]] friend constexpr auto operator+(tagged_ptr lhs, difference_type diff) noexcept {
- lhs += diff;
- return lhs;
- }
-
- [[nodiscard]] friend constexpr auto operator+(difference_type diff, tagged_ptr rhs) noexcept {
- rhs += diff;
- return rhs;
- }
-
- [[nodiscard]] friend constexpr auto operator-(tagged_ptr lhs, difference_type diff) noexcept {
- lhs -= diff;
- return lhs;
- }
-
- [[nodiscard]] friend constexpr auto operator-(difference_type diff, tagged_ptr rhs) noexcept {
- rhs -= diff;
- return rhs;
- }
-
- constexpr auto & operator-=(difference_type diff) noexcept {
- _pointer = tagged_ptr{pointer()-diff, tag()}._pointer;
- return *this;
- }
-
- constexpr auto & operator--() noexcept {
- _pointer = tagged_ptr{pointer()-1u, tag()}._pointer;
- return *this;
- }
-
- [[nodiscard]] constexpr auto operator--(int) noexcept {
- auto copy = auto(*this);
- this->operator--();
- return copy;
- }
-
- // observers
- [[nodiscard]] constexpr dirty_pointer unsafe_dirty_pointer() const noexcept {
- // this function is not intentionally constexpr, as it is needed only to interact with
- // existing runtime code
- return _pointer;
- }
-
- static constexpr bool _support_aliasing_masking = pointer_tagging_schema_with_aliasing<schema>;
-
- [[nodiscard]] constexpr clean_pointer aliasing_pointer() const noexcept {
- if constexpr (_support_aliasing_masking) {
- if !consteval {
- return schema::recover_aliasing_pointer(_pointer);
- }
- }
-
- return schema::recover_pointer(_pointer);
- }
-
- [[nodiscard]] constexpr clean_pointer pointer() const noexcept {
- return schema::recover_pointer(_pointer);
- }
-
- [[nodiscard]] constexpr tag_type tag() const noexcept {
- return schema::recover_tag(_pointer);
- }
-
- template <std::size_t I> [[nodiscard]] friend constexpr decltype(auto) get(tagged_ptr _pair) noexcept {
- static_assert(I < 3);
- if constexpr (I == 0) {
- return _pair.pointer();
- } else {
- return _pair.tag();
- }
- }
-
- [[nodiscard]] constexpr explicit operator bool() const noexcept {
- return pointer() != nullptr;
- }
-
- [[nodiscard]] friend constexpr ptrdiff_t operator-(tagged_ptr lhs, tagged_ptr rhs) noexcept {
- return lhs.pointer() - rhs.pointer();
- }
-
- // comparison operators
- [[nodiscard]] friend bool operator==(tagged_ptr, tagged_ptr) = default;
-
- struct _compare_object {
- clean_pointer pointer;
- tag_type tag;
-
- friend auto operator<=>(_compare_object, _compare_object) = default;
- };
-
- [[nodiscard]] friend constexpr auto operator<=>(tagged_ptr lhs, tagged_ptr rhs) noexcept {
- return _compare_object{lhs.pointer(), lhs.tag()} <=> _compare_object{rhs.pointer(), rhs.tag()};
- }
- [[nodiscard]] friend constexpr bool operator==(tagged_ptr lhs, clean_pointer rhs) noexcept {
- return lhs.pointer() == rhs;
- }
- [[nodiscard]] friend constexpr auto operator<=>(tagged_ptr lhs, clean_pointer rhs) noexcept {
- return lhs.pointer() <=> rhs;
- }
- [[nodiscard]] friend constexpr bool operator==(tagged_ptr lhs, nullptr_t) noexcept {
- return lhs.pointer() == nullptr;
- }
-};
-
-// to_address specialization
-template <typename _T, typename _Tag, typename _Schema> static constexpr auto to_address(tagged_ptr<_T, _Tag, _Schema> p) noexcept -> tagged_ptr<_T, _Tag, _Schema>::element_type * {
- return p.pointer();
-}
-
-// iterator traits
-template <typename _T, typename _Tag, typename _Schema>
-struct _LIBCPP_TEMPLATE_VIS iterator_traits<tagged_ptr<_T, _Tag, _Schema>> {
- using _tagged_ptr = tagged_ptr<_T, _Tag, _Schema>;
-
- using iterator_category = std::random_access_iterator_tag;
- using iterator_concept = std::contiguous_iterator_tag;
-
- using value_type = _tagged_ptr::element_type;
- using reference = value_type &;
- using pointer = _tagged_ptr::clean_pointer;
- using difference_type = _tagged_ptr::difference_type;
-};
-
-// pointer traits
-template <typename _T, typename _Tag, typename _Schema>
-struct _LIBCPP_TEMPLATE_VIS pointer_traits<tagged_ptr<_T, _Tag, _Schema>> {
- using _tagged_ptr = tagged_ptr<_T, _Tag, _Schema>;
- using pointer = _tagged_ptr::clean_pointer;
- using element_type = _tagged_ptr::element_type;
- using difference_type = _tagged_ptr::difference_type;
-
- template <typename _Up> using rebind = typename _tagged_ptr::template rebind<_Up>;
-
- static constexpr uintptr_t unused_bits = _tagged_ptr::dirty_pointer_free_bits;
-
-public:
- _LIBCPP_HIDE_FROM_ABI constexpr static _tagged_ptr pointer_to(pointer ptr) _NOEXCEPT {
- return _tagged_ptr{ptr};
- }
-};
-
-// support for tuple protocol so we can split tagged pointer to structured bindings:
-// auto [ptr, tag] = tagged_ptr
-template <typename _T, typename _Tag, typename _Schema>
-struct tuple_size<tagged_ptr<_T, _Tag, _Schema>>: std::integral_constant<std::size_t, 2> {};
-
-template <std::size_t I, typename _T, typename _Tag, typename _Schema>
-struct tuple_element<I, tagged_ptr<_T, _Tag, _Schema>> {
- using _pair_type = tagged_ptr<_T, _Tag, _Schema>;
- using type = std::conditional_t<I == 0, typename _pair_type::clean_pointer, typename _pair_type::tag_type>;
-};
-
-_LIBCPP_END_NAMESPACE_STD
-
-#endif // _LIBCPP_STD_VER >= 26
-
-#endif // _LIBCPP___TAGGED_PTR_H
diff --git a/libcxx/include/memory b/libcxx/include/memory
index da48b1b4661cb8..91fdbfb41b3dfd 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -970,7 +970,7 @@ template<class Pointer = void, class Smart, class... Args>
#endif
#if _LIBCPP_STD_VER >= 26
-# include <__memory/tagged_ptr.h>
+# include <__memory/pointer_tag_pair.h>
#endif
#include <version>
diff --git a/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp b/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
deleted file mode 100644
index 3c7492fc37afea..00000000000000
--- a/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-//
-
-// <memory>
-
-#include <cassert>
-#include <memory>
-
-//#include "test_macros.h"
-
-template <bool Expected> struct test_helper {
- template <typename Lmbd> auto & operator=(Lmbd && lmbd) {
- if constexpr (Expected) {
- static_assert(Lmbd{}());
- assert(lmbd());
- } else {
- static_assert(!Lmbd{}());
- assert(!lmbd());
- }
-
- return *this;
- }
-};
-
-#define HANA_TEST test_helper<true>{} = []
-#define HANA_FAIL_TEST test_helper<false>{} = []
-#define HANA_ASSERT(expr) do { if (!static_cast<bool>(expr)) { if consteval { throw false; } return false; }} while (false)
-
-template <typename T> struct tagged_range {
- using scheme = std::memory::no_tag;
- using iterator = std::tagged_ptr<const T, bool, scheme>;
-
- const T * start;
- size_t size;
-
- constexpr tagged_range(const T * in, std::size_t sz) noexcept: start{in}, size{sz} { }
-
- constexpr auto begin() const {
- return iterator(start, false);
- }
-
- constexpr auto end() const {
- return iterator(start+size, false);
- }
-};
-
-int main(int, char**) {
-
-HANA_TEST {
- int64_t a = 42;
-
- uintptr_t tag = 0b11u;
- auto tptr = std::tagged_ptr<int64_t, uintptr_t, std::memory::alignment_low_bits_tag>(&a, tag);
-
- HANA_ASSERT(tptr.unsafe_dirty_pointer() != &a);
-
- int64_t * original = tptr.pointer();
- HANA_ASSERT(tag == tptr.tag());
- HANA_ASSERT(original == &a);
-
- auto [p, t] = tptr;
- HANA_ASSERT(p == &a);
- HANA_ASSERT(t == tag);
- return true;
-};
-
-HANA_TEST {
- int64_t a[3] = {1,2,3};
-
- uintptr_t tag = 0b11000111u;
- auto tptr = std::tagged_ptr<int64_t[3], uintptr_t, std::memory::low_byte_tag>(&a, tag);
-
- auto * original = tptr.pointer();
- HANA_ASSERT(tag == tptr.tag());
- HANA_ASSERT(original == &a);
-
- auto [p, t] = tptr;
- HANA_ASSERT(p == &a);
- HANA_ASSERT(t == tag);
-
- HANA_ASSERT(tptr[0] == 1);
- HANA_ASSERT(tptr[1] == 2);
- HANA_ASSERT(tptr[2] == 3);
-
-
- return true;
-};
-
-HANA_TEST {
- int64_t a[3] = {1,2,3};
-
- uintptr_t tag = 0b1111u;
- // this only works on ARM64 as it needs available bits on left position
- auto tptr = std::tagged_ptr<int64_t[3], uintptr_t, std::memory::left_shift_tag<4>>(&a, tag);
-
- auto * original = tptr.pointer();
- HANA_ASSERT(tag == tptr.tag());
- HANA_ASSERT(original == &a);
-
- auto [p, t] = tptr;
- HANA_ASSERT(p == &a);
- HANA_ASSERT(t == tag);
-
- HANA_ASSERT(tptr[0] == 1);
- HANA_ASSERT(tptr[1] == 2);
- HANA_ASSERT(tptr[2] == 3);
-
-
- return true;
-};
-
-
-HANA_TEST {
- int64_t array[8] = {1,2,3,4,5,6,7,8};
- int64_t * ptr = &array[0];
- auto tptr = std::tagged_ptr<int64_t, unsigned, std::memory::alignment_low_bits_tag>{ptr, 0b1u};
- int64_t sum = 0;
- while (tptr != &array[8]) {
- sum += *tptr;
- ++tptr;
- }
- return sum;
-};
-
-HANA_TEST {
- int64_t array[8] = {1,2,3,4,5,6,7,8};
- auto rng = tagged_range(&array[0], 8);
-
- static_assert(std::input_or_output_iterator<decltype(rng.begin())>);
- static_assert(std::input_iterator<decltype(rng.begin())>);
- static_assert(std::forward_iterator<decltype(rng.begin())>);
- static_assert(std::bidirectional_iterator<decltype(rng.begin())>);
- static_assert(std::random_access_iterator<decltype(rng.begin())>);
- //static_assert(std::contiguous_iterator<decltype(rng.begin())>);
-
- static_assert(std::ranges::input_range<decltype(rng)>);
- static_assert(std::ranges::forward_range<decltype(rng)>);
- static_assert(std::ranges::bidirectional_range<decltype(rng)>);
- static_assert(std::ranges::random_access_range<decltype(rng)>);
- //static_assert(std::ranges::contiguous_range<decltype(rng)>);
- //static_assert(std::forward_iterator<tagged_range<int64_t>>);
- //static_assert(std::ranges::bidirectional_<tagged_range<int64_t>>);
-
- int64_t sum = 0;
- for (int64_t v: rng) {
- sum += v;
- }
- return sum;
-};
-
-HANA_TEST {
- int a{42};
- auto tptr = std::tagged_ptr<int, bool, std::memory::alignment_low_bits_tag>{&a, false};
-
- auto cptr = std::const_pointer_cast<const int>(tptr);
-
- HANA_ASSERT(cptr.tag() == false);
- HANA_ASSERT(cptr.pointer() == &a);
-
- return true;
-};
-
-HANA_TEST {
- int a{14};
- int * b = &a;
- auto tptr = std::tagged_ptr<int, uintptr_t, std::memory::alignment_low_bits_tag>(b, 0b1u);
- HANA_ASSERT(tptr.pointer() == &a);
- return true;
-};
-
-HANA_TEST {
- alignas(16) int a{14};
- int * b = &a;
- auto tptr = std::tagged_ptr<int, uintptr_t, std::memory::custom_alignment_tag<16>>(std::overaligned_pointer<16>, b, 0b1u);
- return true;
-};
-
-HANA_TEST {
- int a{14};
- int * b = &a;
- auto tptr = std::tagged_ptr<int, uintptr_t, std::memory::bits_needed_tag<2>>(b, 0b11u);
- return true;
-};
-
-HANA_TEST {
- struct overaligned_int { alignas(16) int x; };
-
- using first_ptraits = std::pointer_traits<overaligned_int *>;
- static_assert((first_ptraits::unused_bits & 0b1111ull) == 0b1111ull);
-
- auto v = overaligned_int{4};
- auto tptr = std::tagged_ptr<overaligned_int, uintptr_t, std::memory::bitmask_tag<0b11>>(&v, 0b11u);
- HANA_ASSERT(tptr.pointer() == &v);
- HANA_ASSERT(tptr.tag() == 0b11u);
-
- using ptraits = std::pointer_traits<decltype(tptr)>;
-
- static_assert((ptraits::unused_bits & 0b1111ull) == 0b1100ull);
-
- return true;
-};
-
- return 0;
-}
-
diff --git a/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp.exec b/libcxx/test/libcxx/memory/tagged_ptr/tagged_ptr.pass.cpp.exec
deleted file mode 100755
index da5dbfc0682484c850eba817089edf178f6db9a7..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 16856
zcmeI4TS!!45XZm0h)R}PRz?cjLl-lSR$AMOt<tiJ;Vq#hgOAlc>UvpMcNZ at R8YSHb
zdaCFp>m}&|rjk!fQV=3Y36&)o(Nlp1Q4b+>nK^qlcQXol?LRPQzWKgy=FI%|yv{ya
zeE%&;2!kLolBSZTn}iq;7Fr3hfwY)Z%8EjV{fND)oOfeI3y$4d=5e04q*7Mfoz=0&
zTD&cGPw0LeZL at N1EK&y4u0VJl_G9x6?hiMUpkHA>602>QMZbQe87YI_PLJ0uJ>L2@
z+WED<Aw8c{4`M$iUvx5BFY!89QdVj|P!j3Yrb8yHiEf|k at YqCi%oCzNg)h=QE1BK8
zZzVsMe6As#)JHzA&y%%~T|rtw8ZCUz`Y*DIf-I!;jfk9}zayu`Q=8-NZgG>(vXPYQ
zn6D`v|B#huK6Pod{lVuKg}Z3aLK^NVnO4c#nm#RIimx$mC#&^GmSr3D$9eeoiWYt^
z_seHm4{Aa at XZ=|V)l-+9t?X2&C{L|cr#1S%aM0%yd;d_kKcKcMMS2L`7Geu2*VAYd
zg1?<C-DkcmnWTI^Y@=GkO_2uy0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X
z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH
z0TB4V2=rG?nB+4aInK}<kG4D$%`?+BvaCFx&SKM^OAnsAPNF&J6m1Gy+viYCEcE0V
zYgU%%@n(+utr(<O%?F&tBnJCvC0iS%+NIV71FCFPU2e5QB(}OdUa^vDqzf#3J=wsM
z)ZzE6)BJD`{9X%9;4e=jwKN>F_$q0FA~(}AQf3<MQCdT)Fq-)V{EBu`Cn*cff7^yF
zMr|}ayz}R0<gWt^0artV>XyDhhvIYj{YstBr*MBFv+2T((EY3_N5<tcYr*uV^>c|Q
zTP|)ow#Ps0kPGRd+kLl2oNW)^)lZmbXJ(p><<7Y8y<@#S19PFX#iw^%|J(V<nWCwJ
z<~yBh%5UBcy-~hC$@M23zp_wSsZ6hZd~L7k=;DceJ^A<M503>C$J0J;9cp^|dbDrB
PF?lY3^6Ko%FVx=;^cc)(
>From 7738999076364d849e9b10bf435ca035aa57c9b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Mon, 13 Jan 2025 18:26:27 +0100
Subject: [PATCH 9/9] update for paper
---
libcxx/include/__memory/pointer_tag_pair.h | 157 ++++++++++-----------
libcxx/include/__memory/pointer_traits.h | 9 --
2 files changed, 71 insertions(+), 95 deletions(-)
diff --git a/libcxx/include/__memory/pointer_tag_pair.h b/libcxx/include/__memory/pointer_tag_pair.h
index 14e8e9773063a2..39d7ad27c3bdfb 100644
--- a/libcxx/include/__memory/pointer_tag_pair.h
+++ b/libcxx/include/__memory/pointer_tag_pair.h
@@ -16,25 +16,26 @@
#if _LIBCPP_STD_VER >= 26
-#include <__assert>
-#include <__bit/bit_width.h>
-#include <__bit/countr.h>
-#include <__config>
-#include <__tuple/tuple_element.h>
-#include <__tuple/tuple_size.h>
-#include <__type_traits/conditional.h>
-#include <__type_traits/is_enum.h>
-#include <__type_traits/is_integral.h>
-#include <__type_traits/is_object.h>
-#include <__type_traits/make_unsigned.h>
-#include <__type_traits/remove_cvref.h>
-#include <__type_traits/underlying_type.h>
-#include <__utility/swap.h>
-#include <climits>
-#include <compare>
+# include <__assert>
+# include <__bit/bit_width.h>
+# include <__bit/countr.h>
+# include <__config>
+# include <__tuple/tuple_element.h>
+# include <__tuple/tuple_size.h>
+# include <__type_traits/conditional.h>
+# include <__type_traits/is_enum.h>
+# include <__type_traits/is_integral.h>
+# include <__type_traits/is_object.h>
+# include <__type_traits/make_unsigned.h>
+# include <__type_traits/remove_cvref.h>
+# include <__type_traits/underlying_type.h>
+# include <__utility/swap.h>
+# include <climits>
+# include <compare>
_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __impl {
constexpr bool __is_ptr_aligned(const void* _ptr, size_t _alignment) noexcept {
# if __has_builtin(__builtin_is_aligned)
return __builtin_is_aligned(_ptr, _alignment);
@@ -44,125 +45,108 @@ constexpr bool __is_ptr_aligned(const void* _ptr, size_t _alignment) noexcept {
}
template <typename T>
-struct __tag_underlying_type {
+struct _underlying_type_or_identity {
using type = T;
};
template <typename T>
requires(std::is_enum_v<T>)
-struct __tag_underlying_type<T> {
+struct _underlying_type_or_identity<T> {
using type = std::underlying_type_t<T>;
};
template <typename T>
-constexpr unsigned _alignment_bits_available = std::countr_zero(alignof(T));
+using _underlying_type_or_identity_t = _underlying_type_or_identity<T>::type;
+} // namespace __impl
-template <typename T, unsigned Bits> struct _few_bits {
- T value : Bits;
-};
-
-template <typename T> struct _few_bits<T, 0> {
- struct zero {
- constexpr zero(T) noexcept { }
- constexpr operator T() const noexcept {
- return T{};
- }
- } value;
-};
-
-template <typename PointeeT, typename TagT, unsigned Bits = _alignment_bits_available<PointeeT>>
-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>>)
+template <typename PointeeT, typename TagT, unsigned Bits = std::countr_zero(alignof(PointeeT))>
+ requires(std::is_object_v<PointeeT> && std::is_unsigned_v<__impl::_underlying_type_or_identity_t<TagT>> &&
+ std::is_same_v<TagT, std::remove_cvref_t<TagT>>)
struct pointer_tag_pair {
public:
- using element_type = PointeeT;
- using pointer_type = PointeeT*;
- using tagged_pointer_type = void *; // or uintptr_t?
- using tag_type = TagT;
+ using element_type = PointeeT;
+ using pointer_type = PointeeT*;
+ using tagged_pointer_type = void*; // I prefer `void *` to avoid roundtrip over an int and losing provenance
+ using tag_type = TagT;
private:
static constexpr unsigned _alignment_needed = (1u << Bits);
static constexpr uintptr_t _tag_mask = (_alignment_needed - 1u);
static constexpr uintptr_t _pointer_mask = ~_tag_mask;
- using _underlaying_tag_type = typename __tag_underlying_type<tag_type>::type;
- // using unsigned_tag_type = std::make_unsigned_t<_underlaying_tag_type>;
- using unspecified_pointer_type = pointer_type;
- using _real_tag_type = _few_bits<_underlaying_tag_type, Bits>;
-
- unspecified_pointer_type _pointer{nullptr}; // required to have size same as sizeof(Pointee *)
+ // for clang it can be just `pointer_type`
+ using _unspecified_pointer_type = pointer_type;
+ _unspecified_pointer_type _pointer{nullptr}; // required to have size same as sizeof(Pointee *)
+ // it doesn't make sense to ask for all or more bits than size of the pointer
static_assert(Bits < sizeof(pointer_type) * CHAR_BIT);
+ // internal constructor for `from_overaligned` and `from_tagged` functions
struct _tagged_t {};
+ constexpr pointer_tag_pair(_tagged_t, _unspecified_pointer_type _ptr) noexcept : _pointer{_ptr} {}
- constexpr pointer_tag_pair(_tagged_t, unspecified_pointer_type _ptr) noexcept : _pointer{_ptr} {}
+ static constexpr auto _pass_thru_mask(tag_type _tag) {
+ auto _value = static_cast<__impl::_underlying_type_or_identity_t<tag_type>>(_tag) & _tag_mask;
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(static_cast<tag_type>(_value) == _tag,
+ "Tag must fit requested bits");
+ return _value;
+ }
+
+ static constexpr auto _from_bitfield_tag(std::size_t _tag) -> tag_type {
+ return static_cast<tag_type>(static_cast<__impl::_underlying_type_or_identity_t<tag_type>>(_tag));
+ }
public:
+ // constructors
pointer_tag_pair() = default; // always noexcept
- constexpr pointer_tag_pair(nullptr_t) noexcept;
+ constexpr pointer_tag_pair(nullptr_t) noexcept: _pointer{nullptr} { }
pointer_tag_pair(const pointer_tag_pair&) = default;
pointer_tag_pair(pointer_tag_pair&&) = default;
pointer_tag_pair& operator=(const pointer_tag_pair&) = default;
pointer_tag_pair& operator=(pointer_tag_pair&&) = default;
- // to store and tag pointer (only if eligible)
- // Precondition: bit_width(static_cast<make_usigned_t<U>>(tag)) <= Bits is true, where U is
- // underlying_type_t<TagType>> if is_enum_v<TagType> and TagType otherwise. Precondition: alignof(ptr) (there is
- // enough of bits) Mandates: alignment of element type >= bits requested This turn them into unsigned.
constexpr pointer_tag_pair(pointer_type _ptr, tag_type _tag)
requires(alignof(element_type) >= _alignment_needed)
{
- const auto _native_tag = _real_tag_type{static_cast<_underlaying_tag_type>(_tag)}.value;
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
- _native_tag == (static_cast<_underlaying_tag_type>(_tag)), "Tag value must fit into requested bits");
- //_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::bit_width(_native_tag) <= Bits, "Tag type value must fits bits
- //requested");
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
- std::__is_ptr_aligned(_ptr, _alignment_needed), "Pointer must be aligned by provided alignment for tagging");
-
- void* _tagged_ptr = __builtin_tag_pointer_mask_or((void*)_ptr, _native_tag, _tag_mask);
- _pointer = static_cast<unspecified_pointer_type>(_tagged_ptr);
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::__impl::__is_ptr_aligned(_ptr, _alignment_needed),
+ "Pointer must be aligned by provided alignment for tagging");
+
+ _pointer = static_cast<_unspecified_pointer_type>(
+ __builtin_tag_pointer_mask_or((void*)_ptr, _pass_thru_mask(_tag), _tag_mask));
}
+
+ // destructor
+ ~pointer_tag_pair() = default;
- // Preconditions: p points to an object X of a type similar ([conv.qual]) to element_type, where X has alignment
- // byte_alignment (inspired by aligned_accessor)
- // The precondition needa to say null pointer value or the thing about pointing to object with aligment. ??
+ // special
template <unsigned _AlignmentPromised>
- static constexpr pointer_tag_pair from_overaligned(pointer_type _ptr, tag_type _tag) {
- const auto _native_tag = _real_tag_type{static_cast<_underlaying_tag_type>(_tag)}.value;
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
- _native_tag == (static_cast<_underlaying_tag_type>(_tag)), "Tag value must fit into requested bits");
- //_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::bit_width(static_cast<unsigned_tag_type>(_tag)) <= Bits, "Tag type
- //value must fits bits requested");
- _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
- std::__is_ptr_aligned(_ptr, _AlignmentPromised), "Pointer must be aligned by provided alignment for tagging");
-
- void* _tagged_ptr = __builtin_tag_pointer_mask_or((void*)_ptr, _native_tag, _tag_mask);
- return pointer_tag_pair{_tagged_t{}, static_cast<unspecified_pointer_type>(_tagged_ptr)};
- }
+ static constexpr pointer_tag_pair from_overaligned(pointer_type _ptr, tag_type _tag)
+ requires(_AlignmentPromised >= _alignment_needed)
+ {
+ _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(std::__impl::__is_ptr_aligned(_ptr, _AlignmentPromised),
+ "Pointer must be aligned by provided alignment for tagging");
- pointer_tag_pair from_tagged(tagged_pointer_type ptr) { // no-constexpr
- // Precondition: valid pointer if untagged??
- return pointer_tag_pair{_tagged_t{}, reinterpret_cast<unspecified_pointer_type>(ptr)};
+ return pointer_tag_pair{_tagged_t{},
+ static_cast<_unspecified_pointer_type>(
+ __builtin_tag_pointer_mask_or((void*)_ptr, _pass_thru_mask(_tag), _tag_mask))};
}
- // destructor
- ~pointer_tag_pair() = default;
+ static pointer_tag_pair from_tagged(tagged_pointer_type ptr) { // no-constexpr
+ // Precondition: valid pointer if untagged
+ return pointer_tag_pair{_tagged_t{}, reinterpret_cast<_unspecified_pointer_type>(ptr)};
+ }
// accessors
- tagged_pointer_type tagged_pointer() const noexcept {
- return reinterpret_cast<tagged_pointer_type>(_pointer);
- }
+ tagged_pointer_type tagged_pointer() const noexcept { return reinterpret_cast<tagged_pointer_type>(_pointer); }
constexpr pointer_type pointer() const noexcept {
return static_cast<pointer_type>(__builtin_tag_pointer_mask((void*)_pointer, _pointer_mask));
}
constexpr tag_type tag() const noexcept {
- const uintptr_t r = __builtin_tag_pointer_mask_as_int((void*)_pointer, _tag_mask);
- return static_cast<tag_type>(_real_tag_type{.value = static_cast<_underlaying_tag_type>(r)}.value);
+ return _from_bitfield_tag(__builtin_tag_pointer_mask_as_int((void*)_pointer, _tag_mask));
}
// swap
- constexpr void swap(pointer_tag_pair& _rhs) noexcept { std::swap(_pointer, _rhs._pointer); }
+ friend constexpr void swap(pointer_tag_pair& _lhs, pointer_tag_pair& _rhs) noexcept { std::swap(_lhs._pointer, _rhs._pointer); }
// comparing {pointer(), tag()} <=> {pointer(), tag()} for consistency
friend constexpr auto operator<=>(pointer_tag_pair lhs, pointer_tag_pair rhs) noexcept {
@@ -173,6 +157,7 @@ struct pointer_tag_pair {
return lhs.tag() <=> rhs.tag();
}
+
friend bool operator==(pointer_tag_pair, pointer_tag_pair) = default;
};
diff --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h
index c8b34e58646831..0914aceb318b74 100644
--- a/libcxx/include/__memory/pointer_traits.h
+++ b/libcxx/include/__memory/pointer_traits.h
@@ -162,15 +162,6 @@ struct _LIBCPP_TEMPLATE_VIS pointer_traits<_Tp*> {
};
#endif
-#if _LIBCPP_STD_VER >= 26
-#if __aarch64__
- static constexpr uintptr_t _upper_bits = 0xFFull << (sizeof(void *) * 8ull) - 8ull;
-#else
- static constexpr uintptr_t _upper_bits = 0ull;
-#endif
- static constexpr uintptr_t unused_bits = (alignof(element_type) - 1ull) | _upper_bits;
-#endif
-
private:
struct __nat {};
More information about the libcxx-commits
mailing list