[libcxx-commits] [libcxx] [libc++] Use relocation in vector::emplace_back (PR #159365)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Wed Sep 17 06:57:33 PDT 2025


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/159365

None

>From 99f785c12c831a9fbbaccdabfe720e8894462058 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 15 Sep 2025 10:37:41 +0200
Subject: [PATCH] [libc++] Use relocation in vector::emplace_back

---
 libcxx/include/__utility/exception_guard.h |  5 ++-
 libcxx/include/__vector/vector.h           | 46 ++++++++++++++++------
 2 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__utility/exception_guard.h b/libcxx/include/__utility/exception_guard.h
index 6fa744e8b4f32..9c1a7b4b124d1 100644
--- a/libcxx/include/__utility/exception_guard.h
+++ b/libcxx/include/__utility/exception_guard.h
@@ -80,7 +80,10 @@ struct __exception_guard_exceptions {
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __complete() _NOEXCEPT { __completed_ = true; }
 
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__exception_guard_exceptions() {
+  // __exception_guard is almost always used with a lamdba, so the destructor is almost certainly unique to the calling
+  // function. LLVM doesn't know that, so it doesn't inline it due to the destructor being in a cold (exception) path.
+  // Annotate the function with always_inline to work around those problems.
+  _LIBCPP_ALWAYS_INLINE _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__exception_guard_exceptions() {
     if (!__completed_)
       __rollback_();
   }
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 27e681aeef22a..483ef4f915453 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -1188,19 +1188,43 @@ _LIBCPP_CONSTEXPR_SINCE_CXX20 inline
     void
 #endif
     vector<_Tp, _Allocator>::emplace_back(_Args&&... __args) {
-  pointer __end = this->__end_;
-  std::__if_likely_else(
-      __end < this->__cap_,
-      [&] {
-        __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
-        ++__end;
-      },
-      [&] { __end = __emplace_back_slow_path(std::forward<_Args>(__args)...); });
-
-  this->__end_ = __end;
+  if constexpr (__libcpp_is_trivially_relocatable<value_type>::value &&
+                __allocator_has_trivial_move_construct_v<allocator_type, value_type> &&
+                __allocator_has_trivial_destroy_v<allocator_type, value_type>) {
+    union _Tmp {
+      _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tmp() {}
+      _LIBCPP_CONSTEXPR_SINCE_CXX20 ~_Tmp() {}
+      value_type __val_;
+    };
+    _Tmp __tmp;
+
+    __alloc_traits::construct(__alloc_, std::addressof(__tmp.__val_), std::forward<_Args>(__args)...);
+
+    auto __guard =
+        std::__make_exception_guard([&, this] { __alloc_traits::destroy(__alloc_, std::addressof(__tmp.__val_)); });
+    std::__if_likely_else(size() != capacity(), [] {}, [this] { reserve(__recommend(size() + 1)); });
+    __guard.__complete();
+    std::__uninitialized_allocator_relocate(
+        __alloc_, std::addressof(__tmp.__val_), std::addressof(__tmp.__val_) + 1, std::__to_address(__end_));
+    ++__end_;
+#if _LIBCPP_STD_VER >= 17
+    return __end_[-1];
+#endif
+  } else {
+    pointer __end = this->__end_;
+    std::__if_likely_else(
+        __end < this->__cap_,
+        [&] {
+          __emplace_back_assume_capacity(std::forward<_Args>(__args)...);
+          ++__end;
+        },
+        [&] { __end = __emplace_back_slow_path(std::forward<_Args>(__args)...); });
+
+    this->__end_ = __end;
 #if _LIBCPP_STD_VER >= 17
-  return *(__end - 1);
+    return *(__end - 1);
 #endif
+  }
 }
 
 template <class _Tp, class _Allocator>



More information about the libcxx-commits mailing list