]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Use aligned new for filesystem::path internals [PR122300]
authorJonathan Wakely <jwakely@redhat.com>
Wed, 4 Mar 2026 10:54:16 +0000 (10:54 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Fri, 6 Mar 2026 09:58:33 +0000 (09:58 +0000)
As Bug 122300 shows, we have at least one target where the
static_assert added by r16-4422-g1b18a9e53960f3 fails. This patch
resurrects the original proposal for using aligned new that I posted in
https://gcc.gnu.org/pipermail/libstdc++/2025-October/063904.html

Instead of just asserting that the memory from operator new will be
sufficiently aligned, check whether it will be and use aligned new if
needed. We don't just use aligned new unconditionally, because that can
add overhead on targets where malloc already meets the requirements.

libstdc++-v3/ChangeLog:

PR libstdc++/122300
* src/c++17/fs_path.cc (path::_List::_Impl): Remove
static_asserts.
(path::_List::_Impl::required_alignment)
(path::_List::_Impl::use_aligned_new): New static data members.
(path::_List::_Impl::create_unchecked): Check use_aligned_new
and use aligned new if needed.
(path::_List::_Impl::alloc_size): New static member function.
(path::_List::_Impl_deleter::operator): Check use_aligned_new
and use aligned delete if needed.

Reviewed-by: Tomasz KamiƄski <tkaminsk@redhat.com>
libstdc++-v3/src/c++17/fs_path.cc

index 9d8d382667895dc640bb5370ec641808a2bf8e03..c217dc27801835d1cf1868f73d65b39d7acfcd2d 100644 (file)
@@ -260,9 +260,12 @@ struct path::_List::_Impl
 
   // We use the two least significant bits to store a _Type value so
   // require memory aligned to at least 4 bytes:
-  static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ >= 4);
-  // Require memory suitably aligned for an _Impl and its value types:
-  static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ >= alignof(value_type));
+  static constexpr size_t required_alignment
+    = std::max(size_t(4), alignof(value_type));
+
+  // Only use aligned new if needed, because it might be less efficient.
+  static constexpr bool use_aligned_new
+    = __STDCPP_DEFAULT_NEW_ALIGNMENT__ < required_alignment;
 
   // Clear the lowest two bits from the pointer (i.e. remove the _Type value)
   static _Impl* notype(_Impl* p)
@@ -297,9 +300,18 @@ struct path::_List::_Impl
   static unique_ptr<_Impl, _Impl_deleter>
   create_unchecked(int n)
   {
-    void* p = ::operator new(sizeof(_Impl) + n * sizeof(value_type));
+    const auto bytes = alloc_size(n);
+    void* p;
+    if constexpr (use_aligned_new)
+      p = ::operator new(bytes, align_val_t{required_alignment});
+    else
+      p = ::operator new(bytes);
     return std::unique_ptr<_Impl, _Impl_deleter>(::new(p) _Impl{n});
   }
+
+  // The number of bytes that must be allocated for _Impl with capacity n.
+  static size_t
+  alloc_size(int n) { return sizeof(_Impl) + n * sizeof(value_type); }
 };
 
 // Destroy and deallocate an _Impl.
@@ -309,9 +321,12 @@ path::_List::_Impl_deleter::operator()(_Impl* p) const noexcept
   p = _Impl::notype(p);
   if (p)
     {
-      const auto n = p->_M_capacity;
+      const auto bytes = _Impl::alloc_size(p->_M_capacity);
       p->~_Impl();
-      ::operator delete(p, sizeof(_Impl) + n * sizeof(_Impl::value_type));
+      if constexpr (_Impl::use_aligned_new)
+       ::operator delete(p, bytes, align_val_t{_Impl::required_alignment});
+      else
+       ::operator delete(p, bytes);
     }
 }