[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