Skip to content

[libc++] Remove some unnecessary functions from __vector_layout - #207152

Merged
philnik777 merged 1 commit into
llvm:mainfrom
philnik777:vector_remove_unnecessary_funcs
Jul 9, 2026
Merged

[libc++] Remove some unnecessary functions from __vector_layout#207152
philnik777 merged 1 commit into
llvm:mainfrom
philnik777:vector_remove_unnecessary_funcs

Conversation

@philnik777

Copy link
Copy Markdown
Contributor

This removes functions which produce identical IR after the first InstCombine pass after inlining compared to their replacements.

@philnik777
philnik777 requested a review from a team as a code owner July 2, 2026 09:55
@philnik777 philnik777 self-assigned this Jul 2, 2026
@llvmorg-github-actions llvmorg-github-actions Bot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jul 2, 2026
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

Changes

This removes functions which produce identical IR after the first InstCombine pass after inlining compared to their replacements.


Full diff: https://github.com/llvm/llvm-project/pull/207152.diff

2 Files Affected:

  • (modified) libcxx/include/__vector/layout.h (-46)
  • (modified) libcxx/include/__vector/vector.h (+8-8)
diff --git a/libcxx/include/__vector/layout.h b/libcxx/include/__vector/layout.h
index 65d11aa238df0..6be670381b244 100644
--- a/libcxx/include/__vector/layout.h
+++ b/libcxx/include/__vector/layout.h
@@ -179,9 +179,6 @@ class __vector_layout {
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type
   __remaining_capacity() const _NOEXCEPT;
 
-  /// Determines if a reallocation is necessary.
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __is_full() const _NOEXCEPT;
-
   /// Sets the member pointing to the first element in the vector to `__new_begin`, the member used
   /// to obtain the vector's bound to the equivalent of `__new_size`, and the member that represents
   /// the vector's capacity to the equivalent to `__new_capacity`.
@@ -202,9 +199,6 @@ class __vector_layout {
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __size() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type __capacity() const _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool __empty() const _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI _Tp& __back() _NOEXCEPT;
-  [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI _Tp const& __back() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __end_ptr() _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_pointer __end_ptr() const _NOEXCEPT;
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI pointer __capacity_ptr() _NOEXCEPT;
@@ -252,11 +246,6 @@ __vector_layout<_Tp, _Alloc>::__remaining_capacity() const _NOEXCEPT {
   return __capacity_ - __size_;
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__is_full() const _NOEXCEPT {
-  return __size_ == __capacity_;
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __vector_layout<_Tp, _Alloc>::__set_layout(
     pointer __new_begin, size_type __new_size, size_type __new_capacity) _NOEXCEPT {
@@ -324,21 +313,6 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return __capacity_;
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
-  return __size_ == 0;
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __vector_layout<_Tp, _Alloc>::__back() _NOEXCEPT {
-  return __begin_[__size_ - 1];
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp const& __vector_layout<_Tp, _Alloc>::__back() const _NOEXCEPT {
-  return __begin_[__size_ - 1];
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {
@@ -384,11 +358,6 @@ __vector_layout<_Tp, _Alloc>::__remaining_capacity() const _NOEXCEPT {
   return __capacity_ - __end_;
 }
 
-template <class _Tp, class _Alloc>
-[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__is_full() const _NOEXCEPT {
-  return __end_ == __capacity_;
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void __vector_layout<_Tp, _Alloc>::__set_layout(
     pointer __new_begin, size_type __new_size, size_type __new_capacity) _NOEXCEPT {
@@ -456,21 +425,6 @@ __vector_layout<_Tp, _Alloc>::__capacity() const _NOEXCEPT {
   return static_cast<size_type>(__capacity_ - __begin_);
 }
 
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 bool __vector_layout<_Tp, _Alloc>::__empty() const _NOEXCEPT {
-  return __begin_ == __end_;
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp& __vector_layout<_Tp, _Alloc>::__back() _NOEXCEPT {
-  return __end_[-1];
-}
-
-template <class _Tp, class _Alloc>
-_LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp const& __vector_layout<_Tp, _Alloc>::__back() const _NOEXCEPT {
-  return __end_[-1];
-}
-
 template <class _Tp, class _Alloc>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __vector_layout<_Tp, _Alloc>::pointer
 __vector_layout<_Tp, _Alloc>::__end_ptr() _NOEXCEPT {
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 2b9508ecafeac..ae55dfbd877c1 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -400,7 +400,7 @@ class vector {
     return __layout_.__capacity();
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
-    return __layout_.__empty();
+    return size() == 0;
   }
 
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI size_type max_size() const _NOEXCEPT {
@@ -442,11 +442,11 @@ class vector {
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI reference back() _NOEXCEPT {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
-    return __layout_.__back();
+    return end()[-1];
   }
   [[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI const_reference back() const _NOEXCEPT {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(!empty(), "back() called on an empty vector");
-    return __layout_.__back();
+    return end()[-1];
   }
 
   //
@@ -1080,7 +1080,7 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Alloc>
 vector<_Tp, _Alloc>::emplace_back(_Args&&... __args) {
   pointer __end = __layout_.__end_ptr();
   std::__if_likely_else(
-      !__layout_.__is_full(),
+      size() != capacity(),
       [&] {
         __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
         ++__end;
@@ -1134,7 +1134,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(__x);
@@ -1157,7 +1157,7 @@ template <class _Tp, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::move(__x));
@@ -1178,7 +1178,7 @@ template <class... _Args>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 typename vector<_Tp, _Allocator>::iterator
 vector<_Tp, _Allocator>::emplace(const_iterator __position, _Args&&... __args) {
   pointer __p = this->__layout_.__begin_ptr() + (__position - begin());
-  if (!__layout_.__is_full()) {
+  if (size() != capacity()) {
     pointer __end = __layout_.__end_ptr();
     if (__p == __end) {
       __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
@@ -1232,7 +1232,7 @@ vector<_Tp, _Allocator>::__insert_with_sentinel(const_iterator __position, _Inpu
   difference_type __off = __position - begin();
   pointer __p           = this->__layout_.__begin_ptr() + __off;
   pointer __old_last    = __layout_.__end_ptr();
-  for (; !__layout_.__is_full() && __first != __last; ++__first)
+  for (; size() != capacity() && __first != __last; ++__first)
     __emplace_back_assume_capacity(*__first);
 
   if (__first == __last)

@ldionne ldionne left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to like that: minimizing the number of functions in the layout classes is a good thing. I had been under the impression that this actually made a difference codegen wise, but I guess it makes sense that it doesn't.

I'd like to look again with green CI and to chat about this during a live review to make sure there's no chance of this regressing perf.

@philnik777
philnik777 force-pushed the vector_remove_unnecessary_funcs branch from 51bd664 to b307228 Compare July 3, 2026 03:02

@ldionne ldionne left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this makes sense. As discussed, you verified these didn't make any difference codegen wise, so I think we could simply not have introduced them in the first place, to keep the layout API minimal.

@philnik777
philnik777 merged commit f6b50ce into llvm:main Jul 9, 2026
78 of 81 checks passed
}
[[__nodiscard__]] _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool empty() const _NOEXCEPT {
return __layout_.__empty();
return size() == 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

size() has to do arithmetic on the pointers, which requires complete types. The LHS, and vector before _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED, didn't require complete types to call empty().

Compete types are only needed if _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED is not defined, which means it is now ABI-dependent if a type has to be complete.

This also means not enabling _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED stops building for us for that reason. (And building with it causes a big size regression that we're currently investigating.)

As @ldionne says in #155330 (review), _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED seems like a risky change. Maybe we shouldn't make it harder to turn it off.

Could we maybe undo this change here to make it easier to compare behavior with the new layout on and off?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't aware of (and didn't expect) this breakage before I read this. It seems like the original _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED patch indeed changed whether vector requires complete types to compute empty(). I can confirm that here: https://godbolt.org/z/EMff4KPjP. This was unintended, and we should fix that at least until we decide we want to change that guarantee. I filed #210732 for that.

I think this should also resolve your concern about different requirements depending on the ABI macro.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I explained in #210732 (comment), it turns out that this is not "a guarantee" we were providing, merely something that happened to work. The Standard actually requires the type to be complete before instantiating any member function of vector<T>.

This doesn't change what I think we should do here: we should return to making it work at least for the LLVM 23 release cause this was unintended and it touches an important type -- but I suspect you should also get started fixing the code that relies on this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nico nico Jul 21, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR!

It seems like the original _LIBCPP_ABI_VECTOR_LAYOUT_SIZE_BASED patch indeed changed whether vector requires complete types to compute empty()

Just to be overly clear (I think you understood it already), but the problem for us was that this PR made the old vector require complete types, while the new one didn't. This made it impossible for us to switch back to the old impl to debug a size increase.

Thanks again for unblocking us :)

@cjdb

cjdb commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

The changes in this patch undo some very delicate fine-tuning that I did in #155330. I'm disappointed that I wasn't asked to review this patch before merging.

@ldionne

ldionne commented Jul 20, 2026

Copy link
Copy Markdown
Member

The changes in this patch undo some very delicate fine-tuning that I did in #155330.

Can you explain? When I reviewed this with Nikolas he explained that he looked at the codegen and all of these are exactly the same after the first InstCombine pass. If that's not the case, then perhaps a revert is mandated, but let's discuss the technical concerns with it.

I'm disappointed that I wasn't asked to review this patch before merging.

Unless mistaken, you are notified on libc++ PRs, so I'm not certain what your expectation was. I don't think it's realistic to put you on the critical path for every vector change going forward since you landed the size-based vector patch.

@philnik777
philnik777 deleted the vector_remove_unnecessary_funcs branch July 21, 2026 10:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants