[libcxx-commits] [libcxx] df361f8 - [libc++] Optimize vector::emplace_back slow path (#222016)
via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Sep 30 04:54:16 PDT 2026
Author: Nikolas Klauser
Date: 2026-09-30T13:54:09+02:00
New Revision: df361f8543d1a2fad3f52532ccc6197fc12e3155
URL: https://github.com/llvm/llvm-project/commit/df361f8543d1a2fad3f52532ccc6197fc12e3155
DIFF: https://github.com/llvm/llvm-project/commit/df361f8543d1a2fad3f52532ccc6197fc12e3155.diff
LOG: [libc++] Optimize vector::emplace_back slow path (#222016)
This improves the code size of the slow path by removing some redundant
checks.
Added:
Modified:
libcxx/include/__numeric/saturation_arithmetic.h
libcxx/include/__vector/vector.h
Removed:
################################################################################
diff --git a/libcxx/include/__numeric/saturation_arithmetic.h b/libcxx/include/__numeric/saturation_arithmetic.h
index 34f784e4a9cf1..f113370c271d0 100644
--- a/libcxx/include/__numeric/saturation_arithmetic.h
+++ b/libcxx/include/__numeric/saturation_arithmetic.h
@@ -26,17 +26,16 @@ _LIBCPP_PUSH_MACROS
_LIBCPP_BEGIN_NAMESPACE_STD
-#if _LIBCPP_STD_VER >= 20
-
-template <__signed_or_unsigned_integer _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp __saturating_add(_Tp __x, _Tp __y) noexcept {
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp __saturating_add(_Tp __x, _Tp __y) _NOEXCEPT {
+ static_assert(__is_signed_integer_v<_Tp> || __is_unsigned_integer_v<_Tp>);
# if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 2101
return __builtin_elementwise_add_sat(__x, __y);
# else
if (_Tp __sum; !__builtin_add_overflow(__x, __y, std::addressof(__sum)))
return __sum;
// Handle overflow
- if constexpr (__unsigned_integer<_Tp>) {
+ if _LIBCPP_CONSTEXPR (__is_unsigned_integer_v<_Tp>) {
return std::numeric_limits<_Tp>::max();
} else {
// Signed addition overflow
@@ -50,6 +49,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr _Tp __saturating_add(_Tp __x, _Tp __y) noexcept
# endif
}
+#if _LIBCPP_STD_VER >= 20
+
template <__signed_or_unsigned_integer _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp __saturating_sub(_Tp __x, _Tp __y) noexcept {
# if defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 2101
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index a93b890e90de1..aa8441e3f9554 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -41,6 +41,7 @@
#include <__memory/swap_allocator.h>
#include <__memory/temp_value.h>
#include <__memory/uninitialized_algorithms.h>
+#include <__numeric/saturation_arithmetic.h>
#include <__ranges/access.h>
#include <__ranges/as_rvalue_view.h>
#include <__ranges/concepts.h>
@@ -60,12 +61,14 @@
#include <__type_traits/is_swappable.h>
#include <__type_traits/remove_const_ref.h>
#include <__type_traits/type_identity.h>
+#include <__utility/assume.h>
#include <__utility/declval.h>
#include <__utility/exception_guard.h>
#include <__utility/forward.h>
#include <__utility/is_pointer_in_range.h>
#include <__utility/move.h>
#include <__utility/pair.h>
+#include <__utility/scope_guard.h>
#include <__utility/swap.h>
#include <initializer_list>
#include <limits>
@@ -882,9 +885,9 @@ vector<_Tp, _Allocator>::__recommend(size_type __new_size) const {
if (__new_size > __ms)
this->__throw_length_error();
const size_type __cap = capacity();
- if (__cap >= __ms / 2)
- return __ms;
- return std::max<size_type>(2 * __cap, __new_size);
+ std::__assume(__cap <= __ms);
+ auto __doubled = std::__saturating_add(__cap, __cap);
+ return std::min(std::max(__new_size, __doubled), __ms);
}
// Default constructs __n objects starting at __layout_.__end_ptr()
@@ -1059,13 +1062,26 @@ vector<_Tp, _Alloc>::emplace_back(_Args&&... __args) {
__self.__emplace_back_assume_capacity(std::forward<_Args>(__largs)...);
},
[](vector& __self, _Args&&... __largs) static {
- _SplitBuffer __v(__self.__recommend(__self.size() + 1), __self.size(), __self.__layout_.__alloc());
- // __v.emplace_back(std::forward<_Args>(__args)...);
- pointer __end = __v.end();
- __alloc_traits::construct(
- __self.__layout_.__alloc(), std::__to_address(__end), std::forward<_Args>(__largs)...);
- __v.__set_sentinel(++__end);
- __self.__layout_.__relocate(__v);
+ __self.__annotate_delete();
+ auto __asan_guard = std::__make_scope_guard([&] { __self.__annotate_new(__self.size()); });
+ auto& __alloc = __self.__layout_.__alloc();
+ auto __size = __self.size();
+ auto __allocation = std::__allocate_at_least(__alloc, __self.__recommend(__size + 1));
+ auto __alloc_guard = std::__make_exception_guard([&] {
+ __alloc_traits::deallocate(__alloc, __allocation.ptr, __allocation.count);
+ });
+ auto __ptr = std::__to_address(__allocation.ptr);
+ auto __old_begin = __self.__layout_.__begin_ptr();
+ __alloc_traits::construct(__alloc, __ptr + __size, std::forward<_Args>(__largs)...);
+ auto __guard = std::__make_exception_guard([&] { __alloc_traits::destroy(__alloc, __ptr + __size); });
+ std::__uninitialized_allocator_relocate(
+ __alloc, std::__to_address(__old_begin), std::__to_address(__self.__layout_.__end_ptr()), __ptr);
+ __guard.__complete();
+ auto __old_cap = __self.capacity();
+ __self.__layout_.__set_layout(__allocation.ptr, __size + 1, __allocation.count);
+ __alloc_guard.__complete();
+ if ((__is_std_allocator_v<allocator_type> && !__libcpp_is_constant_evaluated()) || __old_begin)
+ __alloc_traits::deallocate(__alloc, __old_begin, __old_cap);
},
*this,
std::forward<_Args>(__args)...);
More information about the libcxx-commits
mailing list