[libcxx-commits] [libcxx] [lldb] [libcxx] adds size-based `__split_buffer` representation to unstable ABI (PR #139632)

Christopher Di Bella via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jun 6 10:21:38 PDT 2025


https://github.com/cjdb updated https://github.com/llvm/llvm-project/pull/139632

>From 88bc35049f774c8c3a5c35ff22470a0670ee3e9d Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Wed, 7 May 2025 20:41:56 +0000
Subject: [PATCH 1/5] [libcxx] adds size-based `__split_buffer` representation
 to unstable ABI

**tl;dr** We can significantly improve the runtime performance of
`std::vector` by changing its representation from three pointers to one
pointer and two integers. This document explains the details of this
change, along with the justifications for making it. See the [RFC] for
more information.

`vector` depends on `__split_buffer` for inserting elements. Changing
`__split_buffer` to match `vector`'s representation simplifies the
model, as it eliminates the need to convert between two different
representations of a contiguous buffer in the same configuration of
libc++.

[RFC]: TODO
---
 libcxx/include/__split_buffer    | 549 +++++++++++++++++++++----------
 libcxx/include/__vector/vector.h |  48 +--
 libcxx/include/deque             |  40 +--
 3 files changed, 416 insertions(+), 221 deletions(-)

diff --git a/libcxx/include/__split_buffer b/libcxx/include/__split_buffer
index 21e58f4abc6b3..9710dfec774f7 100644
--- a/libcxx/include/__split_buffer
+++ b/libcxx/include/__split_buffer
@@ -13,10 +13,12 @@
 #include <__algorithm/max.h>
 #include <__algorithm/move.h>
 #include <__algorithm/move_backward.h>
+#include <__assert>
 #include <__config>
 #include <__iterator/distance.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/move_iterator.h>
+#include <__memory/addressof.h>
 #include <__memory/allocate_at_least.h>
 #include <__memory/allocator.h>
 #include <__memory/allocator_traits.h>
@@ -78,23 +80,260 @@ public:
                       __split_buffer,
                       void>;
 
-  pointer __first_;
-  pointer __begin_;
-  pointer __end_;
-  _LIBCPP_COMPRESSED_PAIR(pointer, __cap_, allocator_type, __alloc_);
+  struct __data {
+    pointer __first_ = nullptr;
+    pointer __begin_ = nullptr;
+#ifndef _LIBCPP_ABI_SIZE_BASED_VECTOR
+    pointer __end_ = nullptr;
+    _LIBCPP_COMPRESSED_PAIR(pointer, __cap_ = nullptr, allocator_type, __alloc_);
+#else
+    size_type __size_ = 0;
+    _LIBCPP_COMPRESSED_PAIR(size_type, __cap_ = 0, allocator_type, __alloc_);
+#endif
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __data() = default;
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit __data(const allocator_type& __alloc)
+    : __alloc_(__alloc)
+    {}
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer first() noexcept {
+      return __first_;
+    }
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer first() const noexcept {
+      return __first_;
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer begin() noexcept {
+      return __begin_;
+    }
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer begin() const noexcept {
+      return __begin_;
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer end() noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      return __begin_ + __size_;
+#else
+      return __end_;
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer end() const noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      return __begin_ + __size_;
+#else
+      return __end_;
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type size() const noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      return __size_;
+#else
+      return static_cast<size_type>(__end_ - __begin_);
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      return __size_ == 0;
+#else
+      return __begin_ == __end_;
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type capacity() const noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      return __cap_;
+#else
+      return static_cast<size_type>(__cap_ - __first_);
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_as_pointer() noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      return __first_ + __cap_;
+#else
+      return __cap_;
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_as_pointer() const noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      return __first_ + __cap_;
+#else
+      return __cap_;
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_begin(pointer __new_begin) noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      __size_ -= __new_begin - __begin_;
+#else
+      // TODO: explain why there isn't a pointer-based analogue
+#endif
+
+      __begin_ = __new_begin;
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __front_spare() const noexcept {
+      return static_cast<size_type>(__begin_ - __first_);
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_sentinel(pointer __new_end) noexcept {
+      _LIBCPP_ASSERT(__first_ <= __new_end, "__new_end cannot precede __first_");
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      __size_ += __new_end - end();
+#else
+      __end_ = __new_end;
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_sentinel(size_type __new_size) noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      __size_ = __new_size;
+#else
+      __end_ = __begin_ + __new_size;
+#endif
+    }
 
-  __split_buffer(const __split_buffer&)            = delete;
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_capacity(size_type __new_capacity) noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      __cap_ = __new_capacity;
+#else
+      __cap_ = __first_ + __new_capacity;
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __back_spare() const noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      // `__cap_ - __end_` tells us the total number of spares when in size-mode. We need to remove
+      // the __front_spare from the count.
+      return __cap_ - __size_ - __front_spare();
+#else
+      return static_cast<size_type>(__cap_ - __end_);
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      return __begin_[__size_ - 1];
+#else
+      return *(__end_ - 1);
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      return __begin_[__size_ - 1];
+#else
+      return *(__end_ - 1);
+#endif
+    }
+
+    template<class _Data2>
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __swap_without_allocator(_Data2& __other) noexcept {
+      std::swap(__first_, __other.__first_);
+      std::swap(__begin_, __other.__begin_);
+      std::swap(__cap_, __other.__cap_);
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      std::swap(__size_, __other.__size_);
+#else
+      std::swap(__end_, __other.__end_);
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(__data& __other) noexcept {
+      __swap_without_allocator(__other);
+      std::__swap_allocator(__alloc_, __other.__alloc_);
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __invariants() const noexcept {
+      if (__first_ == nullptr) {
+        if (__begin_ != nullptr) {
+          return false;
+        }
+
+        if (!empty()) {
+          return false;
+        }
+
+        if (capacity() != 0) {
+          return false;
+        }
+
+        return true;
+      }
+
+      if (__begin_ < __first_) {
+        return false;
+      }
+
+      if (capacity() < size()) {
+        return false;
+      }
+
+      if (end() < __begin_) {
+        return false;
+      }
+
+      return true;
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __is_full() const noexcept {
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      return __size_ == __cap_;
+#else
+      return __end_ == __cap_;
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __reset() noexcept {
+      __first_ = nullptr;
+      __begin_ = nullptr;
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      __size_ = 0;
+      __cap_ = 0;
+#else
+      __end_ = nullptr;
+      __cap_ = nullptr;
+#endif
+    }
+
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __copy_without_alloc(__data const& __other)
+      noexcept(is_nothrow_copy_assignable<pointer>::value)
+    {
+      __first_ = __other.__first_;
+      __begin_ = __other.__begin_;
+      __cap_ = __other.__cap_;
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      __size_ = __other.__size_;
+#else
+      __end_ = __other.__end_;
+#endif
+    }
+  };
+
+  __data __data_;
+
+  __split_buffer(const __split_buffer&) = delete;
   __split_buffer& operator=(const __split_buffer&) = delete;
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer()
-      _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
-      : __first_(nullptr), __begin_(nullptr), __end_(nullptr), __cap_(nullptr) {}
+    _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
+  : __data_{}
+  {}
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit __split_buffer(__alloc_rr& __a)
-      : __first_(nullptr), __begin_(nullptr), __end_(nullptr), __cap_(nullptr), __alloc_(__a) {}
+    _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
+  : __data_(__a)
+  {}
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit __split_buffer(const __alloc_rr& __a)
-      : __first_(nullptr), __begin_(nullptr), __end_(nullptr), __cap_(nullptr), __alloc_(__a) {}
+    _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
+  : __data_(__a)
+  {}
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI
   __split_buffer(size_type __cap, size_type __start, __alloc_rr& __a);
@@ -111,36 +350,22 @@ public:
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI ~__split_buffer();
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return __begin_; }
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return __begin_; }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator begin() _NOEXCEPT { return __data_.begin(); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator begin() const _NOEXCEPT { return __data_.begin(); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return __data_.end(); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return __data_.end(); }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator end() _NOEXCEPT { return __end_; }
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_iterator end() const _NOEXCEPT { return __end_; }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __destruct_at_end(__data_.__begin_); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type size() const { return __data_.size(); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const { return __data_.empty(); }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __destruct_at_end(__begin_); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type capacity() const { return __data_.capacity(); }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type size() const {
-    return static_cast<size_type>(__end_ - __begin_);
-  }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference front() { return *__data_.__begin_; }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference front() const { return *__data_.__begin_; }
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const { return __end_ == __begin_; }
-
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type capacity() const {
-    return static_cast<size_type>(__cap_ - __first_);
-  }
-
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __front_spare() const {
-    return static_cast<size_type>(__begin_ - __first_);
-  }
-
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __back_spare() const {
-    return static_cast<size_type>(__cap_ - __end_);
-  }
-
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference front() { return *__begin_; }
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference front() const { return *__begin_; }
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() { return *(__end_ - 1); }
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const { return *(__end_ - 1); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() { return __data_.back(); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const { return __data_.back(); }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void shrink_to_fit() _NOEXCEPT;
 
@@ -149,8 +374,8 @@ public:
   template <class... _Args>
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void emplace_back(_Args&&... __args);
 
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void pop_front() { __destruct_at_begin(__begin_ + 1); }
-  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void pop_back() { __destruct_at_end(__end_ - 1); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void pop_front() { __destruct_at_begin(__data_.begin() + 1); }
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void pop_back() { __destruct_at_end(__data_.end() - 1); }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __construct_at_end(size_type __n);
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __construct_at_end(size_type __n, const_reference __x);
@@ -185,66 +410,52 @@ public:
       _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__alloc_rr>);
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __invariants() const;
-
 private:
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__split_buffer& __c, true_type)
       _NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value) {
-    __alloc_ = std::move(__c.__alloc_);
+    __data_.__alloc_ = std::move(__c.__data_.__alloc_);
   }
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__split_buffer&, false_type) _NOEXCEPT {}
 
   struct _ConstructTransaction {
     _LIBCPP_CONSTEXPR_SINCE_CXX20
-    _LIBCPP_HIDE_FROM_ABI explicit _ConstructTransaction(pointer* __p, size_type __n) _NOEXCEPT
-        : __pos_(*__p),
-          __end_(*__p + __n),
-          __dest_(__p) {}
+    _LIBCPP_HIDE_FROM_ABI explicit _ConstructTransaction(__split_buffer* __parent, pointer __p, size_type __n) noexcept
+    : __pos_(__p),
+      __end_(__p + __n),
+      __parent_(__parent) {}
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI ~_ConstructTransaction() { *__dest_ = __pos_; }
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI ~_ConstructTransaction() {
+      __parent_->__data_.__update_sentinel(__pos_);
+    }
 
     pointer __pos_;
     const pointer __end_;
 
   private:
-    pointer* __dest_;
+    __split_buffer* __parent_;
   };
 };
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __split_buffer<_Tp, _Allocator>::__invariants() const {
-  if (__first_ == nullptr) {
-    if (__begin_ != nullptr)
-      return false;
-    if (__end_ != nullptr)
-      return false;
-    if (__cap_ != nullptr)
-      return false;
-  } else {
-    if (__begin_ < __first_)
-      return false;
-    if (__end_ < __begin_)
-      return false;
-    if (__cap_ < __end_)
-      return false;
-  }
-  return true;
+  return __data_.__invariants();
 }
 
-//  Default constructs __n objects starting at __end_
+//  Default constructs __n objects starting at `__begin_ + size()`
 //  throws if construction throws
 //  Precondition:  __n > 0
 //  Precondition:  size() + __n <= capacity()
 //  Postcondition:  size() == size() + __n
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::__construct_at_end(size_type __n) {
-  _ConstructTransaction __tx(std::addressof(this->__end_), __n);
+  _ConstructTransaction __tx(this, __data_.end(), __n);
   for (; __tx.__pos_ != __tx.__end_; ++__tx.__pos_) {
-    __alloc_traits::construct(__alloc_, std::__to_address(__tx.__pos_));
+    __alloc_traits::construct(__data_.__alloc_, std::__to_address(__tx.__pos_));
   }
 }
 
-//  Copy constructs __n objects starting at __end_ from __x
+//  Copy constructs __n objects starting at `__begin_ + size()` from __x
 //  throws if construction throws
 //  Precondition:  __n > 0
 //  Precondition:  size() + __n <= capacity()
@@ -253,30 +464,35 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::__construct_
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 __split_buffer<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x) {
-  _ConstructTransaction __tx(std::addressof(this->__end_), __n);
+  _ConstructTransaction __tx(this, __data_.end(), __n);
   for (; __tx.__pos_ != __tx.__end_; ++__tx.__pos_) {
-    __alloc_traits::construct(__alloc_, std::__to_address(__tx.__pos_), __x);
+    __alloc_traits::construct(__data_.__alloc_, std::__to_address(__tx.__pos_), __x);
   }
 }
 
-template <class _Tp, class _Allocator>
-template <class _Iterator, class _Sentinel>
+template<class _Tp, class _Allocator>
+template<class _Iterator, class _Sentinel>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 __split_buffer<_Tp, _Allocator>::__construct_at_end_with_sentinel(_Iterator __first, _Sentinel __last) {
-  __alloc_rr& __a = __alloc_;
+  __alloc_rr& __a = __data_.__alloc_;
   for (; __first != __last; ++__first) {
-    if (__end_ == __cap_) {
-      size_type __old_cap = __cap_ - __first_;
+    if (__data_.__back_spare() == 0) {
+      size_type __old_cap = __data_.capacity();
       size_type __new_cap = std::max<size_type>(2 * __old_cap, 8);
       __split_buffer __buf(__new_cap, 0, __a);
-      for (pointer __p = __begin_; __p != __end_; ++__p, (void)++__buf.__end_)
-        __alloc_traits::construct(__buf.__alloc_, std::__to_address(__buf.__end_), std::move(*__p));
+      pointer __buf_end = __buf.__data_.end();
+      pointer __end = __data_.end();
+      for (pointer __p = __data_.__begin_; __p != __end; ++__p, (void)++__buf_end)
+        __alloc_traits::construct(__buf.__data_.__alloc_, std::__to_address(__buf_end), std::move(*__p));
+      __buf.__data_.__update_sentinel(__buf_end);
       swap(__buf);
     }
-    __alloc_traits::construct(__a, std::__to_address(this->__end_), *__first);
-    ++this->__end_;
+
+    __alloc_traits::construct(__a, std::__to_address(__data_.end()), *__first);
+    __data_.__update_sentinel(size() + 1);
   }
 }
+
 template <class _Tp, class _Allocator>
 template <class _ForwardIterator, __enable_if_t<__has_forward_iterator_category<_ForwardIterator>::value, int> >
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void
@@ -288,92 +504,82 @@ template <class _Tp, class _Allocator>
 template <class _ForwardIterator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 __split_buffer<_Tp, _Allocator>::__construct_at_end_with_size(_ForwardIterator __first, size_type __n) {
-  _ConstructTransaction __tx(std::addressof(this->__end_), __n);
+  _ConstructTransaction __tx(this, __data_.end(), __n);
   for (; __tx.__pos_ != __tx.__end_; ++__tx.__pos_, (void)++__first) {
-    __alloc_traits::construct(__alloc_, std::__to_address(__tx.__pos_), *__first);
+    __alloc_traits::construct(__data_.__alloc_, std::__to_address(__tx.__pos_), *__first);
   }
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline void
 __split_buffer<_Tp, _Allocator>::__destruct_at_begin(pointer __new_begin, false_type) {
-  while (__begin_ != __new_begin)
-    __alloc_traits::destroy(__alloc_, std::__to_address(__begin_++));
+  pointer __begin = __data_.__begin_;
+  while (__begin != __new_begin)
+    __alloc_traits::destroy(__data_.__alloc_, std::__to_address(__begin++));
+  __data_.__update_begin(__begin);
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline void
 __split_buffer<_Tp, _Allocator>::__destruct_at_begin(pointer __new_begin, true_type) {
-  __begin_ = __new_begin;
+  __data_.__update_begin(__new_begin);
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI void
 __split_buffer<_Tp, _Allocator>::__destruct_at_end(pointer __new_last, false_type) _NOEXCEPT {
-  while (__new_last != __end_)
-    __alloc_traits::destroy(__alloc_, std::__to_address(--__end_));
-}
-
-template <class _Tp, class _Allocator>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI void
-__split_buffer<_Tp, _Allocator>::__destruct_at_end(pointer __new_last, true_type) _NOEXCEPT {
-  __end_ = __new_last;
+  pointer __end = __data_.end();
+  while (__new_last != __end)
+    __alloc_traits::destroy(__data_.__alloc_, std::__to_address(--__end));
+  __data_.__update_sentinel(__end);
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20
 __split_buffer<_Tp, _Allocator>::__split_buffer(size_type __cap, size_type __start, __alloc_rr& __a)
-    : __cap_(nullptr), __alloc_(__a) {
-  if (__cap == 0) {
-    __first_ = nullptr;
-  } else {
-    auto __allocation = std::__allocate_at_least(__alloc_, __cap);
-    __first_          = __allocation.ptr;
-    __cap             = __allocation.count;
+  : __data_(__a) {
+  _LIBCPP_ASSERT(__cap >= __start, "can't have a start point outside the capacity");
+  if (__cap > 0) {
+    auto __allocation = std::__allocate_at_least(__data_.__alloc_, __cap);
+    __data_.__first_ = __allocation.ptr;
+    __cap            = __allocation.count;
   }
-  __begin_ = __end_ = __first_ + __start;
-  __cap_            = __first_ + __cap;
+
+  __data_.__begin_ = __data_.__first_ + __start;
+  __data_.__update_sentinel(__data_.__begin_);
+  __data_.__update_capacity(__cap);
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 __split_buffer<_Tp, _Allocator>::~__split_buffer() {
   clear();
-  if (__first_)
-    __alloc_traits::deallocate(__alloc_, __first_, capacity());
+  if (__data_.__first_)
+    __alloc_traits::deallocate(__data_.__alloc_, __data_.__first_, capacity());
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 __split_buffer<_Tp, _Allocator>::__split_buffer(__split_buffer&& __c)
     _NOEXCEPT_(is_nothrow_move_constructible<allocator_type>::value)
-    : __first_(std::move(__c.__first_)),
-      __begin_(std::move(__c.__begin_)),
-      __end_(std::move(__c.__end_)),
-      __cap_(std::move(__c.__cap_)),
-      __alloc_(std::move(__c.__alloc_)) {
-  __c.__first_ = nullptr;
-  __c.__begin_ = nullptr;
-  __c.__end_   = nullptr;
-  __c.__cap_   = nullptr;
+    : __data_(std::move(__c.__data_)) {
+  __c.__data_.__reset();
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20
 __split_buffer<_Tp, _Allocator>::__split_buffer(__split_buffer&& __c, const __alloc_rr& __a)
-    : __cap_(nullptr), __alloc_(__a) {
-  if (__a == __c.__alloc_) {
-    __first_     = __c.__first_;
-    __begin_     = __c.__begin_;
-    __end_       = __c.__end_;
-    __cap_       = __c.__cap_;
-    __c.__first_ = nullptr;
-    __c.__begin_ = nullptr;
-    __c.__end_   = nullptr;
-    __c.__cap_   = nullptr;
+    : __data_(__a) {
+  if (__a == __c.__data_.__alloc_) {
+    __data_.__first_ = __c.__data_.__first_;
+    __data_.__begin_ = __c.__data_.__begin_;
+    __data_.__update_sentinel(__c.__data_.end());
+    __data_.__update_capacity(__c.__data_.capacity());
+    __c.__data_.__reset();
   } else {
-    auto __allocation = std::__allocate_at_least(__alloc_, __c.size());
-    __first_          = __allocation.ptr;
-    __begin_ = __end_ = __first_;
-    __cap_            = __first_ + __allocation.count;
+    auto __allocation = std::__allocate_at_least(__data_.__alloc_, __c.size());
+    __data_.__first_ = __allocation.ptr;
+    __data_.__begin_ = __data_.__first_;
+    __data_.__update_sentinel(__data_.__first_);
+    __data_.__update_capacity(__allocation.count);
     typedef move_iterator<iterator> _Ip;
     __construct_at_end(_Ip(__c.begin()), _Ip(__c.end()));
   }
@@ -387,23 +593,16 @@ __split_buffer<_Tp, _Allocator>::operator=(__split_buffer&& __c)
                !__alloc_traits::propagate_on_container_move_assignment::value) {
   clear();
   shrink_to_fit();
-  __first_ = __c.__first_;
-  __begin_ = __c.__begin_;
-  __end_   = __c.__end_;
-  __cap_   = __c.__cap_;
+  __data_.__copy_without_alloc(__c.__data_);
   __move_assign_alloc(__c, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
-  __c.__first_ = __c.__begin_ = __c.__end_ = __c.__cap_ = nullptr;
+  __c.__data_.__reset();
   return *this;
 }
 
 template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::swap(__split_buffer& __x)
     _NOEXCEPT_(!__alloc_traits::propagate_on_container_swap::value || __is_nothrow_swappable_v<__alloc_rr>) {
-  std::swap(__first_, __x.__first_);
-  std::swap(__begin_, __x.__begin_);
-  std::swap(__end_, __x.__end_);
-  std::swap(__cap_, __x.__cap_);
-  std::__swap_allocator(__alloc_, __x.__alloc_);
+  __data_.swap(__x.__data_);
 }
 
 template <class _Tp, class _Allocator>
@@ -412,14 +611,11 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::shrink_to_fi
 #if _LIBCPP_HAS_EXCEPTIONS
     try {
 #endif // _LIBCPP_HAS_EXCEPTIONS
-      __split_buffer<value_type, __alloc_rr&> __t(size(), 0, __alloc_);
+      __split_buffer<value_type, __alloc_rr&> __t(size(), 0, __data_.__alloc_);
       if (__t.capacity() < capacity()) {
-        __t.__construct_at_end(move_iterator<pointer>(__begin_), move_iterator<pointer>(__end_));
-        __t.__end_ = __t.__begin_ + (__end_ - __begin_);
-        std::swap(__first_, __t.__first_);
-        std::swap(__begin_, __t.__begin_);
-        std::swap(__end_, __t.__end_);
-        std::swap(__cap_, __t.__cap_);
+        __t.__construct_at_end(move_iterator<pointer>(__data_.__begin_), move_iterator<pointer>(__data_.end()));
+        __t.__data_.__update_sentinel(__data_.size());
+        __data_.__swap_without_allocator(__t.__data_);
       }
 #if _LIBCPP_HAS_EXCEPTIONS
     } catch (...) {
@@ -431,52 +627,61 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::shrink_to_fi
 template <class _Tp, class _Allocator>
 template <class... _Args>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::emplace_front(_Args&&... __args) {
-  if (__begin_ == __first_) {
-    if (__end_ < __cap_) {
-      difference_type __d = __cap_ - __end_;
+  if (__data_.__begin_ == __data_.__first_) {
+    pointer __end = __data_.end();
+    if (__data_.__front_spare() != 0) {
+      difference_type __d = __data_.__back_spare();
       __d                 = (__d + 1) / 2;
-      __begin_            = std::move_backward(__begin_, __end_, __end_ + __d);
-      __end_ += __d;
+      __data_.__begin_    = std::move_backward(__data_.__begin_, __end, __end + __d);
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+      // TODO: explain why there isn't a size-based analogue
+#else
+      __data_.__end_ += __d;
+#endif
     } else {
-      size_type __c = std::max<size_type>(2 * static_cast<size_type>(__cap_ - __first_), 1);
-      __split_buffer<value_type, __alloc_rr&> __t(__c, (__c + 3) / 4, __alloc_);
-      __t.__construct_at_end(move_iterator<pointer>(__begin_), move_iterator<pointer>(__end_));
-      std::swap(__first_, __t.__first_);
-      std::swap(__begin_, __t.__begin_);
-      std::swap(__end_, __t.__end_);
-      std::swap(__cap_, __t.__cap_);
+      size_type __c = std::max<size_type>(2 * capacity(), 1);
+      __split_buffer<value_type, __alloc_rr&> __t(__c, (__c + 3) / 4, __data_.__alloc_);
+      __t.__construct_at_end(move_iterator<pointer>(__data_.__begin_), move_iterator<pointer>(__end));
+      __data_.__swap_without_allocator(__t.__data_);
     }
   }
-  __alloc_traits::construct(__alloc_, std::__to_address(__begin_ - 1), std::forward<_Args>(__args)...);
-  --__begin_;
+
+  __alloc_traits::construct(__data_.__alloc_, std::__to_address(__data_.__begin_ - 1), std::forward<_Args>(__args)...);
+  --__data_.__begin_;
+#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+  __data_.__update_sentinel(__data_.end() + 1);
+#else
+  // TODO: explain why there isn't a pointer-based analogue
+#endif
 }
 
-template <class _Tp, class _Allocator>
-template <class... _Args>
+template<class _Tp, class _Allocator>
+template<class... _Args>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::emplace_back(_Args&&... __args) {
-  if (__end_ == __cap_) {
-    if (__begin_ > __first_) {
-      difference_type __d = __begin_ - __first_;
+  pointer __end = __data_.end();
+  if (__data_.__back_spare() == 0) {
+    if (__data_.__begin_ > __data_.__first_) {
+      difference_type __d = __data_.__begin_ - __data_.__first_;
       __d                 = (__d + 1) / 2;
-      __end_              = std::move(__begin_, __end_, __begin_ - __d);
-      __begin_ -= __d;
+      __end               = std::move(__data_.__begin_, __end, __data_.__begin_ - __d);
+      __data_.__begin_   -= __d;
+      __data_.__update_sentinel(__end);
     } else {
-      size_type __c = std::max<size_type>(2 * static_cast<size_type>(__cap_ - __first_), 1);
-      __split_buffer<value_type, __alloc_rr&> __t(__c, __c / 4, __alloc_);
-      __t.__construct_at_end(move_iterator<pointer>(__begin_), move_iterator<pointer>(__end_));
-      std::swap(__first_, __t.__first_);
-      std::swap(__begin_, __t.__begin_);
-      std::swap(__end_, __t.__end_);
-      std::swap(__cap_, __t.__cap_);
+      size_type __c = std::max<size_type>(2 * capacity(), 1);
+      __split_buffer<value_type, __alloc_rr&> __t(__c, __c / 4, __data_.__alloc_);
+      __t.__construct_at_end(move_iterator<pointer>(__data_.__begin_), move_iterator<pointer>(__end));
+      __data_.__swap_without_allocator(__t.__data_);
     }
   }
-  __alloc_traits::construct(__alloc_, std::__to_address(__end_), std::forward<_Args>(__args)...);
-  ++__end_;
+
+  __alloc_traits::construct(__data_.__alloc_, std::__to_address(__end), std::forward<_Args>(__args)...);
+  __data_.__update_sentinel(++__end);
 }
 
-template <class _Tp, class _Allocator>
+template<class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 inline _LIBCPP_HIDE_FROM_ABI void
-swap(__split_buffer<_Tp, _Allocator>& __x, __split_buffer<_Tp, _Allocator>& __y) _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y))) {
+swap(__split_buffer<_Tp, _Allocator>& __x, __split_buffer<_Tp, _Allocator>& __y) _NOEXCEPT_(_NOEXCEPT_(__x.swap(__y)))
+{
   __x.swap(__y);
 }
 
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 4e0d76fbbe3de..0aec0e21cdece 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -841,15 +841,16 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 vector<_Tp, _Allocator>::__swap_out_circular_buffer(__split_buffer<value_type, allocator_type&>& __v) {
   __annotate_delete();
-  auto __new_begin = __v.__begin_ - (__end_ - __begin_);
+  auto __new_begin = __v.begin() - size();
   std::__uninitialized_allocator_relocate(
       this->__alloc_, std::__to_address(__begin_), std::__to_address(__end_), std::__to_address(__new_begin));
-  __v.__begin_ = __new_begin;
+  __v.__data_.__begin_ = __new_begin;
   __end_       = __begin_; // All the objects have been destroyed by relocating them.
-  std::swap(this->__begin_, __v.__begin_);
-  std::swap(this->__end_, __v.__end_);
-  std::swap(this->__cap_, __v.__cap_);
-  __v.__first_ = __v.__begin_;
+
+  std::swap(this->__begin_, __v.__data_.__begin_);
+  std::swap(this->__end_, __v.__data_.__end_);
+  std::swap(this->__cap_, __v.__data_.__cap_);
+  __v.__data_.__first_ = __v.begin();
   __annotate_new(size());
 }
 
@@ -861,25 +862,25 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::pointer
 vector<_Tp, _Allocator>::__swap_out_circular_buffer(__split_buffer<value_type, allocator_type&>& __v, pointer __p) {
   __annotate_delete();
-  pointer __ret = __v.__begin_;
+  pointer __ret = __v.begin();
 
   // Relocate [__p, __end_) first to avoid having a hole in [__begin_, __end_)
   // in case something in [__begin_, __p) throws.
   std::__uninitialized_allocator_relocate(
-      this->__alloc_, std::__to_address(__p), std::__to_address(__end_), std::__to_address(__v.__end_));
-  __v.__end_ += (__end_ - __p);
+      this->__alloc_, std::__to_address(__p), std::__to_address(__end_), std::__to_address(__v.end()));
+  auto __relocated_so_far = __end_ - __p;
+  __v.__data_.__update_sentinel(__v.end() + __relocated_so_far);
   __end_           = __p; // The objects in [__p, __end_) have been destroyed by relocating them.
-  auto __new_begin = __v.__begin_ - (__p - __begin_);
+  auto __new_begin = __v.begin() - (__p - __begin_);
 
   std::__uninitialized_allocator_relocate(
       this->__alloc_, std::__to_address(__begin_), std::__to_address(__p), std::__to_address(__new_begin));
-  __v.__begin_ = __new_begin;
+  __v.__data_.__begin_ = __new_begin;
   __end_       = __begin_; // All the objects have been destroyed by relocating them.
-
-  std::swap(this->__begin_, __v.__begin_);
-  std::swap(this->__end_, __v.__end_);
-  std::swap(this->__cap_, __v.__cap_);
-  __v.__first_ = __v.__begin_;
+  std::swap(this->__begin_, __v.__data_.__begin_);
+  std::swap(this->__end_, __v.__data_.__end_);
+  std::swap(this->__cap_, __v.__data_.__cap_);
+  __v.__data_.__first_ = __v.begin();
   __annotate_new(size());
   return __ret;
 }
@@ -1127,8 +1128,9 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::pointer
 vector<_Tp, _Allocator>::__emplace_back_slow_path(_Args&&... __args) {
   __split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), size(), this->__alloc_);
   //    __v.emplace_back(std::forward<_Args>(__args)...);
-  __alloc_traits::construct(this->__alloc_, std::__to_address(__v.__end_), std::forward<_Args>(__args)...);
-  __v.__end_++;
+  pointer __end = __v.end();
+  __alloc_traits::construct(this->__alloc_, std::__to_address(__end), std::forward<_Args>(__args)...);
+  __v.__data_.__update_sentinel(++__end);
   __swap_out_circular_buffer(__v);
   return this->__end_;
 }
@@ -1303,14 +1305,14 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu
     __split_buffer<value_type, allocator_type&> __merged(
         __recommend(size() + __v.size()), __off, __alloc_); // has `__off` positions available at the front
     std::__uninitialized_allocator_relocate(
-        __alloc_, std::__to_address(__old_last), std::__to_address(this->__end_), std::__to_address(__merged.__end_));
+        __alloc_, std::__to_address(__old_last), std::__to_address(this->__end_), std::__to_address(__merged.end()));
     __guard.__complete(); // Release the guard once objects in [__old_last_, __end_) have been successfully relocated.
-    __merged.__end_ += this->__end_ - __old_last;
+    __merged.__data_.__update_sentinel(__merged.end() + (this->__end_ - __old_last));
     this->__end_ = __old_last;
     std::__uninitialized_allocator_relocate(
-        __alloc_, std::__to_address(__v.__begin_), std::__to_address(__v.__end_), std::__to_address(__merged.__end_));
-    __merged.__end_ += __v.size();
-    __v.__end_ = __v.__begin_;
+        __alloc_, std::__to_address(__v.begin()), std::__to_address(__v.end()), std::__to_address(__merged.end()));
+    __merged.__data_.__update_sentinel(__merged.size() + __v.size());
+    __v.__data_.__update_sentinel(__v.begin());
     __p        = __swap_out_circular_buffer(__merged, __p);
   }
   return __make_iter(__p);
diff --git a/libcxx/include/deque b/libcxx/include/deque
index d8645d06ae59e..2545622f53740 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -1240,7 +1240,7 @@ private:
       shrink_to_fit();
     }
     __alloc()       = __c.__alloc();
-    __map_.__alloc_ = __c.__map_.__alloc_;
+    __map_.__data_.__alloc_ = __c.__map_.__data_.__alloc_;
   }
 
   _LIBCPP_HIDE_FROM_ABI void __copy_assign_alloc(const deque&, false_type) {}
@@ -1319,7 +1319,7 @@ deque<_Tp, _Allocator>::deque(const deque& __c)
     : __map_(__pointer_allocator(__alloc_traits::select_on_container_copy_construction(__c.__alloc()))),
       __start_(0),
       __size_(0),
-      __alloc_(__map_.__alloc_) {
+      __alloc_(__map_.__data_.__alloc_) {
   __annotate_new(0);
   __append(__c.begin(), __c.end());
 }
@@ -2058,7 +2058,7 @@ void deque<_Tp, _Allocator>::__add_front_capacity() {
   else if (__map_.size() < __map_.capacity()) { // we can put the new buffer into the map, but don't shift things around
     // until all buffers are allocated.  If we throw, we don't need to fix
     // anything up (any added buffers are undetectible)
-    if (__map_.__front_spare() > 0)
+    if (__map_.__data_.__front_spare() > 0)
       __map_.emplace_front(__alloc_traits::allocate(__a, __block_size));
     else {
       __map_.emplace_back(__alloc_traits::allocate(__a, __block_size));
@@ -2072,7 +2072,7 @@ void deque<_Tp, _Allocator>::__add_front_capacity() {
   // Else need to allocate 1 buffer, *and* we need to reallocate __map_.
   else {
     __split_buffer<pointer, __pointer_allocator&> __buf(
-        std::max<size_type>(2 * __map_.capacity(), 1), 0, __map_.__alloc_);
+        std::max<size_type>(2 * __map_.capacity(), 1), 0, __map_.__data_.__alloc_);
 
     typedef __allocator_destructor<_Allocator> _Dp;
     unique_ptr<pointer, _Dp> __hold(__alloc_traits::allocate(__a, __block_size), _Dp(__a, __block_size));
@@ -2081,10 +2081,7 @@ void deque<_Tp, _Allocator>::__add_front_capacity() {
 
     for (__map_pointer __i = __map_.begin(); __i != __map_.end(); ++__i)
       __buf.emplace_back(*__i);
-    std::swap(__map_.__first_, __buf.__first_);
-    std::swap(__map_.__begin_, __buf.__begin_);
-    std::swap(__map_.__end_, __buf.__end_);
-    std::swap(__map_.__cap_, __buf.__cap_);
+    __map_.__data_.__swap_without_allocator(__buf.__data_);
     __start_ = __map_.size() == 1 ? __block_size / 2 : __start_ + __block_size;
   }
   __annotate_whole_block(0, __asan_poison);
@@ -2115,7 +2112,7 @@ void deque<_Tp, _Allocator>::__add_front_capacity(size_type __n) {
     // until all buffers are allocated.  If we throw, we don't need to fix
     // anything up (any added buffers are undetectible)
     for (; __nb > 0; --__nb, __start_ += __block_size - (__map_.size() == 1)) {
-      if (__map_.__front_spare() == 0)
+      if (__map_.__data_.__front_spare() == 0)
         break;
       __map_.emplace_front(__alloc_traits::allocate(__a, __block_size));
       __annotate_whole_block(0, __asan_poison);
@@ -2135,7 +2132,7 @@ void deque<_Tp, _Allocator>::__add_front_capacity(size_type __n) {
   else {
     size_type __ds = (__nb + __back_capacity) * __block_size - __map_.empty();
     __split_buffer<pointer, __pointer_allocator&> __buf(
-        std::max<size_type>(2 * __map_.capacity(), __nb + __map_.size()), 0, __map_.__alloc_);
+        std::max<size_type>(2 * __map_.capacity(), __nb + __map_.size()), 0, __map_.__data_.__alloc_);
 #  if _LIBCPP_HAS_EXCEPTIONS
     try {
 #  endif // _LIBCPP_HAS_EXCEPTIONS
@@ -2158,10 +2155,7 @@ void deque<_Tp, _Allocator>::__add_front_capacity(size_type __n) {
     }
     for (__map_pointer __i = __map_.begin(); __i != __map_.end(); ++__i)
       __buf.emplace_back(*__i);
-    std::swap(__map_.__first_, __buf.__first_);
-    std::swap(__map_.__begin_, __buf.__begin_);
-    std::swap(__map_.__end_, __buf.__end_);
-    std::swap(__map_.__cap_, __buf.__cap_);
+    __map_.__data_.__swap_without_allocator(__buf.__data_);
     __start_ += __ds;
   }
 }
@@ -2181,7 +2175,7 @@ void deque<_Tp, _Allocator>::__add_back_capacity() {
   else if (__map_.size() < __map_.capacity()) { // we can put the new buffer into the map, but don't shift things around
     // until it is allocated.  If we throw, we don't need to fix
     // anything up (any added buffers are undetectible)
-    if (__map_.__back_spare() != 0)
+    if (__map_.__data_.__back_spare() != 0)
       __map_.emplace_back(__alloc_traits::allocate(__a, __block_size));
     else {
       __map_.emplace_front(__alloc_traits::allocate(__a, __block_size));
@@ -2195,7 +2189,7 @@ void deque<_Tp, _Allocator>::__add_back_capacity() {
   // Else need to allocate 1 buffer, *and* we need to reallocate __map_.
   else {
     __split_buffer<pointer, __pointer_allocator&> __buf(
-        std::max<size_type>(2 * __map_.capacity(), 1), __map_.size(), __map_.__alloc_);
+        std::max<size_type>(2 * __map_.capacity(), 1), __map_.size(), __map_.__data_.__alloc_);
 
     typedef __allocator_destructor<_Allocator> _Dp;
     unique_ptr<pointer, _Dp> __hold(__alloc_traits::allocate(__a, __block_size), _Dp(__a, __block_size));
@@ -2204,10 +2198,7 @@ void deque<_Tp, _Allocator>::__add_back_capacity() {
 
     for (__map_pointer __i = __map_.end(); __i != __map_.begin();)
       __buf.emplace_front(*--__i);
-    std::swap(__map_.__first_, __buf.__first_);
-    std::swap(__map_.__begin_, __buf.__begin_);
-    std::swap(__map_.__end_, __buf.__end_);
-    std::swap(__map_.__cap_, __buf.__cap_);
+    __map_.__data_.__swap_without_allocator(__buf.__data_);
     __annotate_whole_block(__map_.size() - 1, __asan_poison);
   }
 }
@@ -2237,7 +2228,7 @@ void deque<_Tp, _Allocator>::__add_back_capacity(size_type __n) {
     // until all buffers are allocated.  If we throw, we don't need to fix
     // anything up (any added buffers are undetectible)
     for (; __nb > 0; --__nb) {
-      if (__map_.__back_spare() == 0)
+      if (__map_.__data_.__back_spare() == 0)
         break;
       __map_.emplace_back(__alloc_traits::allocate(__a, __block_size));
       __annotate_whole_block(__map_.size() - 1, __asan_poison);
@@ -2260,7 +2251,7 @@ void deque<_Tp, _Allocator>::__add_back_capacity(size_type __n) {
     __split_buffer<pointer, __pointer_allocator&> __buf(
         std::max<size_type>(2 * __map_.capacity(), __nb + __map_.size()),
         __map_.size() - __front_capacity,
-        __map_.__alloc_);
+        __map_.__data_.__alloc_);
 #  if _LIBCPP_HAS_EXCEPTIONS
     try {
 #  endif // _LIBCPP_HAS_EXCEPTIONS
@@ -2283,10 +2274,7 @@ void deque<_Tp, _Allocator>::__add_back_capacity(size_type __n) {
     }
     for (__map_pointer __i = __map_.end(); __i != __map_.begin();)
       __buf.emplace_front(*--__i);
-    std::swap(__map_.__first_, __buf.__first_);
-    std::swap(__map_.__begin_, __buf.__begin_);
-    std::swap(__map_.__end_, __buf.__end_);
-    std::swap(__map_.__cap_, __buf.__cap_);
+    __map_.__data_.__swap_without_allocator(__buf.__data_);
     __start_ -= __ds;
   }
 }

>From 13551cb4f2fa16c6322b9acb67e5b201c6f70af3 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Tue, 13 May 2025 18:31:30 +0000
Subject: [PATCH 2/5] fixes CI issues

---
 libcxx/include/__split_buffer       | 54 ++++++++++++++---------------
 libcxx/utils/gdb/libcxx/printers.py |  2 +-
 2 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/libcxx/include/__split_buffer b/libcxx/include/__split_buffer
index 9710dfec774f7..0cd1a0e360c59 100644
--- a/libcxx/include/__split_buffer
+++ b/libcxx/include/__split_buffer
@@ -97,21 +97,21 @@ public:
     : __alloc_(__alloc)
     {}
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer first() noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer first() _NOEXCEPT {
       return __first_;
     }
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer first() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer first() const _NOEXCEPT {
       return __first_;
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer begin() noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer begin() _NOEXCEPT {
       return __begin_;
     }
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer begin() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer begin() const _NOEXCEPT {
       return __begin_;
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer end() noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer end() _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       return __begin_ + __size_;
 #else
@@ -119,7 +119,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer end() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer end() const _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       return __begin_ + __size_;
 #else
@@ -127,7 +127,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type size() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       return __size_;
 #else
@@ -135,7 +135,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       return __size_ == 0;
 #else
@@ -143,7 +143,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type capacity() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type capacity() const _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       return __cap_;
 #else
@@ -151,7 +151,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_as_pointer() noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_as_pointer() _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       return __first_ + __cap_;
 #else
@@ -159,7 +159,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_as_pointer() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_as_pointer() const _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       return __first_ + __cap_;
 #else
@@ -167,7 +167,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_begin(pointer __new_begin) noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_begin(pointer __new_begin) _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       __size_ -= __new_begin - __begin_;
 #else
@@ -177,11 +177,11 @@ public:
       __begin_ = __new_begin;
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __front_spare() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __front_spare() const _NOEXCEPT {
       return static_cast<size_type>(__begin_ - __first_);
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_sentinel(pointer __new_end) noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_sentinel(pointer __new_end) _NOEXCEPT {
       _LIBCPP_ASSERT(__first_ <= __new_end, "__new_end cannot precede __first_");
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       __size_ += __new_end - end();
@@ -190,7 +190,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_sentinel(size_type __new_size) noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_sentinel(size_type __new_size) _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       __size_ = __new_size;
 #else
@@ -198,7 +198,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_capacity(size_type __new_capacity) noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_capacity(size_type __new_capacity) _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       __cap_ = __new_capacity;
 #else
@@ -206,7 +206,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __back_spare() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __back_spare() const _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       // `__cap_ - __end_` tells us the total number of spares when in size-mode. We need to remove
       // the __front_spare from the count.
@@ -216,7 +216,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       return __begin_[__size_ - 1];
 #else
@@ -224,7 +224,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       return __begin_[__size_ - 1];
 #else
@@ -233,7 +233,7 @@ public:
     }
 
     template<class _Data2>
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __swap_without_allocator(_Data2& __other) noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __swap_without_allocator(_Data2& __other) _NOEXCEPT {
       std::swap(__first_, __other.__first_);
       std::swap(__begin_, __other.__begin_);
       std::swap(__cap_, __other.__cap_);
@@ -244,12 +244,12 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(__data& __other) noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void swap(__data& __other) _NOEXCEPT {
       __swap_without_allocator(__other);
       std::__swap_allocator(__alloc_, __other.__alloc_);
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __invariants() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __invariants() const _NOEXCEPT {
       if (__first_ == nullptr) {
         if (__begin_ != nullptr) {
           return false;
@@ -281,7 +281,7 @@ public:
       return true;
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __is_full() const noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __is_full() const _NOEXCEPT {
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
       return __size_ == __cap_;
 #else
@@ -289,7 +289,7 @@ public:
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __reset() noexcept {
+    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __reset() _NOEXCEPT {
       __first_ = nullptr;
       __begin_ = nullptr;
 #ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
@@ -302,7 +302,7 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __copy_without_alloc(__data const& __other)
-      noexcept(is_nothrow_copy_assignable<pointer>::value)
+      _NOEXCEPT_(is_nothrow_copy_assignable<pointer>::value)
     {
       __first_ = __other.__first_;
       __begin_ = __other.__begin_;
@@ -322,7 +322,7 @@ public:
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __split_buffer()
     _NOEXCEPT_(is_nothrow_default_constructible<allocator_type>::value)
-  : __data_{}
+  : __data_()
   {}
 
   _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI explicit __split_buffer(__alloc_rr& __a)
@@ -420,7 +420,7 @@ private:
 
   struct _ConstructTransaction {
     _LIBCPP_CONSTEXPR_SINCE_CXX20
-    _LIBCPP_HIDE_FROM_ABI explicit _ConstructTransaction(__split_buffer* __parent, pointer __p, size_type __n) noexcept
+    _LIBCPP_HIDE_FROM_ABI explicit _ConstructTransaction(__split_buffer* __parent, pointer __p, size_type __n) _NOEXCEPT
     : __pos_(__p),
       __end_(__p + __n),
       __parent_(__parent) {}
diff --git a/libcxx/utils/gdb/libcxx/printers.py b/libcxx/utils/gdb/libcxx/printers.py
index 31c27a1959cb2..8194f584ea137 100644
--- a/libcxx/utils/gdb/libcxx/printers.py
+++ b/libcxx/utils/gdb/libcxx/printers.py
@@ -426,7 +426,7 @@ class StdDequePrinter(object):
     def __init__(self, val):
         self.val = val
         self.size = int(val["__size_"])
-        self.start_ptr = self.val["__map_"]["__begin_"]
+        self.start_ptr = self.val["__map_"]["__data_"]["__begin_"]
         self.first_block_start_index = int(self.val["__start_"])
         self.node_type = self.start_ptr.type
         self.block_size = self._calculate_block_size(val.type.template_argument(0))

>From 6d3ec2b72a51d032fa81f52e29066f274b31fc5b Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Tue, 27 May 2025 17:51:55 +0000
Subject: [PATCH 3/5] applies suggestions

---
 libcxx/include/__split_buffer | 86 ++++++++++++-----------------------
 1 file changed, 29 insertions(+), 57 deletions(-)

diff --git a/libcxx/include/__split_buffer b/libcxx/include/__split_buffer
index 0cd1a0e360c59..e9bad689992c0 100644
--- a/libcxx/include/__split_buffer
+++ b/libcxx/include/__split_buffer
@@ -83,12 +83,13 @@ public:
   struct __data {
     pointer __first_ = nullptr;
     pointer __begin_ = nullptr;
-#ifndef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
+    size_type __size_ = 0;
+    size_type __cap_ = 0;
+    allocator_type __alloc_;
+#else
     pointer __end_ = nullptr;
     _LIBCPP_COMPRESSED_PAIR(pointer, __cap_ = nullptr, allocator_type, __alloc_);
-#else
-    size_type __size_ = 0;
-    _LIBCPP_COMPRESSED_PAIR(size_type, __cap_ = 0, allocator_type, __alloc_);
 #endif
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI __data() = default;
@@ -97,13 +98,6 @@ public:
     : __alloc_(__alloc)
     {}
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer first() _NOEXCEPT {
-      return __first_;
-    }
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer first() const _NOEXCEPT {
-      return __first_;
-    }
-
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer begin() _NOEXCEPT {
       return __begin_;
     }
@@ -112,7 +106,7 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer end() _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       return __begin_ + __size_;
 #else
       return __end_;
@@ -120,7 +114,7 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer end() const _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       return __begin_ + __size_;
 #else
       return __end_;
@@ -128,7 +122,7 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type size() const _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       return __size_;
 #else
       return static_cast<size_type>(__end_ - __begin_);
@@ -136,7 +130,7 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       return __size_ == 0;
 #else
       return __begin_ == __end_;
@@ -144,31 +138,15 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type capacity() const _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       return __cap_;
 #else
       return static_cast<size_type>(__cap_ - __first_);
 #endif
     }
 
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_as_pointer() _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
-      return __first_ + __cap_;
-#else
-      return __cap_;
-#endif
-    }
-
-    _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_as_pointer() const _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
-      return __first_ + __cap_;
-#else
-      return __cap_;
-#endif
-    }
-
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_begin(pointer __new_begin) _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       __size_ -= __new_begin - __begin_;
 #else
       // TODO: explain why there isn't a pointer-based analogue
@@ -183,7 +161,7 @@ public:
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_sentinel(pointer __new_end) _NOEXCEPT {
       _LIBCPP_ASSERT(__first_ <= __new_end, "__new_end cannot precede __first_");
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       __size_ += __new_end - end();
 #else
       __end_ = __new_end;
@@ -191,7 +169,7 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_sentinel(size_type __new_size) _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       __size_ = __new_size;
 #else
       __end_ = __begin_ + __new_size;
@@ -199,7 +177,7 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __update_capacity(size_type __new_capacity) _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       __cap_ = __new_capacity;
 #else
       __cap_ = __first_ + __new_capacity;
@@ -207,7 +185,7 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __back_spare() const _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       // `__cap_ - __end_` tells us the total number of spares when in size-mode. We need to remove
       // the __front_spare from the count.
       return __cap_ - __size_ - __front_spare();
@@ -217,7 +195,7 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       return __begin_[__size_ - 1];
 #else
       return *(__end_ - 1);
@@ -225,7 +203,7 @@ public:
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       return __begin_[__size_ - 1];
 #else
       return *(__end_ - 1);
@@ -237,7 +215,7 @@ public:
       std::swap(__first_, __other.__first_);
       std::swap(__begin_, __other.__begin_);
       std::swap(__cap_, __other.__cap_);
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       std::swap(__size_, __other.__size_);
 #else
       std::swap(__end_, __other.__end_);
@@ -251,38 +229,32 @@ public:
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __invariants() const _NOEXCEPT {
       if (__first_ == nullptr) {
-        if (__begin_ != nullptr) {
+        if (__begin_ != nullptr)
           return false;
-        }
 
-        if (!empty()) {
+        if (!empty())
           return false;
-        }
 
-        if (capacity() != 0) {
+        if (capacity() != 0)
           return false;
-        }
 
         return true;
       }
 
-      if (__begin_ < __first_) {
+      if (__begin_ < __first_)
         return false;
-      }
 
-      if (capacity() < size()) {
+      if (capacity() < size())
         return false;
-      }
 
-      if (end() < __begin_) {
+      if (end() < __begin_)
         return false;
-      }
 
       return true;
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __is_full() const _NOEXCEPT {
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       return __size_ == __cap_;
 #else
       return __end_ == __cap_;
@@ -292,7 +264,7 @@ public:
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void __reset() _NOEXCEPT {
       __first_ = nullptr;
       __begin_ = nullptr;
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       __size_ = 0;
       __cap_ = 0;
 #else
@@ -307,7 +279,7 @@ public:
       __first_ = __other.__first_;
       __begin_ = __other.__begin_;
       __cap_ = __other.__cap_;
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       __size_ = __other.__size_;
 #else
       __end_ = __other.__end_;
@@ -633,7 +605,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::emplace_fron
       difference_type __d = __data_.__back_spare();
       __d                 = (__d + 1) / 2;
       __data_.__begin_    = std::move_backward(__data_.__begin_, __end, __end + __d);
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
       // TODO: explain why there isn't a size-based analogue
 #else
       __data_.__end_ += __d;
@@ -648,7 +620,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 void __split_buffer<_Tp, _Allocator>::emplace_fron
 
   __alloc_traits::construct(__data_.__alloc_, std::__to_address(__data_.__begin_ - 1), std::forward<_Args>(__args)...);
   --__data_.__begin_;
-#ifdef _LIBCPP_ABI_SIZE_BASED_VECTOR
+#ifdef _LIBCPP_ABI_SIZE_BASED_CONTAINERS
   __data_.__update_sentinel(__data_.end() + 1);
 #else
   // TODO: explain why there isn't a pointer-based analogue

>From 7084c96db07cb60f2e63ca973730f85f09870d00 Mon Sep 17 00:00:00 2001
From: Christopher Di Bella <cjdb at google.com>
Date: Thu, 29 May 2025 21:57:15 +0000
Subject: [PATCH 4/5] updates formatting

---
 libcxx/include/__split_buffer | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/libcxx/include/__split_buffer b/libcxx/include/__split_buffer
index e9bad689992c0..1e7d33fd433da 100644
--- a/libcxx/include/__split_buffer
+++ b/libcxx/include/__split_buffer
@@ -239,18 +239,18 @@ public:
           return false;
 
         return true;
-      }
-
-      if (__begin_ < __first_)
-        return false;
+      } else {
+        if (__begin_ < __first_)
+          return false;
 
-      if (capacity() < size())
-        return false;
+        if (capacity() < size())
+          return false;
 
-      if (end() < __begin_)
-        return false;
+        if (end() < __begin_)
+          return false;
 
-      return true;
+        return true;
+      }
     }
 
     _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __is_full() const _NOEXCEPT {

>From af7c901f6a457cef23677cdc82ce6d3950a4c00b Mon Sep 17 00:00:00 2001
From: Jorge Gorbe Moya <jgorbe at google.com>
Date: Tue, 3 Jun 2025 19:39:38 +0000
Subject: [PATCH 5/5] changes deque's pretty-printer to work with new
 __split_buffer

---
 lldb/examples/synthetic/libcxx.py | 54 ++++++++++++++++++++++++-------
 1 file changed, 42 insertions(+), 12 deletions(-)

diff --git a/lldb/examples/synthetic/libcxx.py b/lldb/examples/synthetic/libcxx.py
index 5abeb3061f4f5..521bd06af5b3f 100644
--- a/lldb/examples/synthetic/libcxx.py
+++ b/lldb/examples/synthetic/libcxx.py
@@ -1,3 +1,6 @@
+from enum import Enum
+from sys import stderr
+import sys
 import lldb
 import lldb.formatters.Logger
 
@@ -74,6 +77,40 @@ def stdstring_SummaryProvider(valobj, dict):
             return '"' + strval + '"'
 
 
+def get_buffer_data(parent):
+    map_valobj = parent.valobj.GetChildMemberWithName("__map_")
+    map_data = map_valobj.GetChildMemberWithName("__data_")
+    if map_data.IsValid():
+        return map_data
+
+    return map_valobj
+
+
+def get_buffer_end(buffer, begin):
+    map_end = buffer.GetChildMemberWithName("__end_")
+    if map_end.IsValid():
+        return map_end.GetValueAsUnsigned(0)
+    map_size = buffer.GetChildMemberWithName("__size_").GetValueAsUnsigned(0)
+    return begin + map_size
+
+
+def get_buffer_endcap(parent, buffer, begin, has_compressed_pair_layout, is_size_based):
+    if has_compressed_pair_layout:
+        map_endcap = parent._get_value_of_compressed_pair(
+            buffer.GetChildMemberWithName("__end_cap_")
+        )
+    else:
+        map_endcap = buffer.GetChildMemberWithName("__cap_")
+        if not map_endcap.IsValid():
+            map_endcap = buffer.GetChildMemberWithName("__end_cap_")
+        map_endcap = map_endcap.GetValueAsUnsigned(0)
+
+    if is_size_based:
+        return begin + map_endcap
+
+    return map_endcap
+
+
 class stdvector_SynthProvider:
     def __init__(self, valobj, dict):
         logger = lldb.formatters.Logger.Logger()
@@ -755,23 +792,16 @@ def update(self):
             if self.block_size < 0:
                 logger.write("block_size < 0")
                 return
-            map_ = self.valobj.GetChildMemberWithName("__map_")
             start = self.valobj.GetChildMemberWithName("__start_").GetValueAsUnsigned(0)
+
+            map_ = get_buffer_data(self)
+            is_size_based = map_.GetChildMemberWithName("__size_").IsValid()
             first = map_.GetChildMemberWithName("__first_")
             map_first = first.GetValueAsUnsigned(0)
             self.map_begin = map_.GetChildMemberWithName("__begin_")
             map_begin = self.map_begin.GetValueAsUnsigned(0)
-            map_end = map_.GetChildMemberWithName("__end_").GetValueAsUnsigned(0)
-
-            if has_compressed_pair_layout:
-                map_endcap = self._get_value_of_compressed_pair(
-                    map_.GetChildMemberWithName("__end_cap_")
-                )
-            else:
-                map_endcap = map_.GetChildMemberWithName("__cap_")
-                if not map_endcap.IsValid():
-                    map_endcap = map_.GetChildMemberWithName("__end_cap_")
-                map_endcap = map_endcap.GetValueAsUnsigned(0)
+            map_end = get_buffer_end(map_, map_begin)
+            map_endcap = get_buffer_endcap(self, map_, map_begin, has_compressed_pair_layout, is_size_based)
 
             # check consistency
             if not map_first <= map_begin <= map_end <= map_endcap:



More information about the libcxx-commits mailing list