[libcxx-commits] [libcxx] e49c8a0 - [libc++] Base string's alignment on __STDCPP_DEFAULT_NEW_ALIGNMENT__ (#171785)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jul 2 22:11:09 PDT 2026


Author: Nikolas Klauser
Date: 2026-07-03T07:11:04+02:00
New Revision: e49c8a0363267c7b7235aabb3d36ac50e4281c26

URL: https://github.com/llvm/llvm-project/commit/e49c8a0363267c7b7235aabb3d36ac50e4281c26
DIFF: https://github.com/llvm/llvm-project/commit/e49c8a0363267c7b7235aabb3d36ac50e4281c26.diff

LOG: [libc++] Base string's alignment on __STDCPP_DEFAULT_NEW_ALIGNMENT__ (#171785)

This allows users to influence how much we overalign `string`s
allocations and tune it to the new/delete implementation via
`-fnew-alignment`. If we don't have `__STDCPP_DEFAULT_NEW_ALGINMENT__`
or we're not using `std::allocator`, we default to an alignment of
`sizeof(void*)`.

Added: 
    libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp

Modified: 
    libcxx/include/__cxx03/string
    libcxx/include/string
    libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
    libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
    libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
    libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
    libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
    libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__cxx03/string b/libcxx/include/__cxx03/string
index a919a59e55d57..e5330fb01c04f 100644
--- a/libcxx/include/__cxx03/string
+++ b/libcxx/include/__cxx03/string
@@ -1623,7 +1623,18 @@ private:
   static _LIBCPP_HIDE_FROM_ABI size_type __align_it(size_type __s) _NOEXCEPT {
     return (__s + (__a - 1)) & ~(__a - 1);
   }
-  enum { __alignment = 8 };
+
+  // GCC currently doesn't define __STDCPP_DEFAULT_NEW_ALIGNMENT__ unconditionally
+  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123872
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+  static inline const size_t
+      __alignment = __is_default_allocator<allocator_type>::value && __STDCPP_DEFAULT_NEW_ALIGNMENT__ > sizeof(void*)
+                      ? __STDCPP_DEFAULT_NEW_ALIGNMENT__
+                      : sizeof(void*);
+#else
+  static inline const size_t __alignment = sizeof(void*);
+#endif
+
   static _LIBCPP_HIDE_FROM_ABI size_type __recommend(size_type __s) _NOEXCEPT {
     if (__s < __min_cap) {
       return static_cast<size_type>(__min_cap) - 1;

diff  --git a/libcxx/include/string b/libcxx/include/string
index 6516d26e288a9..a297a05813cee 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -2276,7 +2276,17 @@ private:
   static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __align_it(size_type __s) _NOEXCEPT {
     return (__s + (__a - 1)) & ~(__a - 1);
   }
-  enum { __alignment = 8 };
+
+  // GCC currently doesn't define __STDCPP_DEFAULT_NEW_ALIGNMENT__ unconditionally
+  // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123872
+#  ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+  static inline const size_t
+      __alignment = __is_std_allocator_v<allocator_type> && __STDCPP_DEFAULT_NEW_ALIGNMENT__ > sizeof(void*)
+                      ? __STDCPP_DEFAULT_NEW_ALIGNMENT__
+                      : sizeof(void*);
+#  else
+  static inline const size_t __alignment = sizeof(void*);
+#  endif
 
   // This makes sure that we're using a capacity with some extra alignment, since allocators almost always over-align
   // the allocations anyways, improving memory usage. More importantly, this ensures that the lowest bit is never set

diff  --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 77da29225957b..367a58897cec0 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -15,8 +15,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alginment = 8;
+#endif
 
 int main(int, char**) {
   std::string input_string;

diff  --git a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
index 73825ef4845f2..15736e4a1ceed 100644
--- a/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx-03/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -17,8 +17,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-static const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alginment = 8;
+#endif
 
 template <class = int>
 TEST_CONSTEXPR_CXX20 void full_size() {

diff  --git a/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp b/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp
new file mode 100644
index 0000000000000..f8906c9fb9827
--- /dev/null
+++ b/libcxx/test/libcxx/strings/basic.string/new_alignment.sh.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Check that std::string bases its alignment on __STDCPP_DEFAULT_NEW_ALIGNMENT__
+
+// UNSUPPORTED: c++03
+
+// RUN: %{build} -faligned-new=32 -DALIGN=32
+// RUN: %{run}
+// RUN: %{build} -faligned-new=16 -DALIGN=16
+// RUN: %{run}
+// RUN: %{build} -faligned-new=8 -DALIGN=8
+// RUN: %{run}
+
+#include <cassert>
+#include <string>
+
+int main(int, char**) {
+  std::string str;
+  str.reserve(25);
+
+  assert(str.capacity() % ALIGN == ALIGN - 1);
+
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
index 77da29225957b..3c16701cbf860 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp
@@ -8,6 +8,13 @@
 
 // <string>
 
+// This test is sensitive what __STDCPP_DEFAULT_NEW_ALIGNMENT__ is set to. Enable aligned-new for GCC so that GCC
+// defines the macro
+// ADDITIONAL_COMPILE_FLAGS(gcc): -faligned-new
+
+// The allocations are done in the dylib, so we need to use an up-to-date one
+// XFAIL: using-built-library-before-llvm-23
+
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
@@ -15,22 +22,21 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
-const std::size_t alignment = 8;
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const std::size_t alignment = 8;
+#endif
 
 int main(int, char**) {
-  std::string input_string;
-  input_string.resize(64, 'a');
-
-  // Call a constructor which selects its size using __recommend.
-  std::string test_string(input_string.data());
+  std::string str(64, 'a');
   const std::size_t expected_align8_size = 71;
 
   // Demonstrate the lesser capacity/allocation size when the alignment requirement is 8.
   if (alignment == 8) {
-    assert(test_string.capacity() == expected_align8_size);
+    assert(str.capacity() == expected_align8_size);
   } else {
-    assert(test_string.capacity() == expected_align8_size + 8);
+    assert(str.capacity() == expected_align8_size + 8);
   }
 
   return 0;

diff  --git a/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
index 6bfcb5d4bfcd8..11bd1a5928d4a 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -17,8 +17,11 @@
 
 #include "test_macros.h"
 
-// alignment of the string heap buffer is hardcoded to 8
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const std::size_t alignment = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
 static const std::size_t alignment = 8;
+#endif
 
 template <class = int>
 TEST_CONSTEXPR_CXX20 void full_size() {

diff  --git a/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
index 45a52acab464e..b35bed39f2bed 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/max_size.pass.cpp
@@ -8,6 +8,10 @@
 
 // UNSUPPORTED: no-exceptions
 
+// This test is sensitive what __STDCPP_DEFAULT_NEW_ALIGNMENT__ is set to. Enable aligned-new for GCC so that GCC
+// defines the macro
+// ADDITIONAL_COMPILE_FLAGS(gcc): -faligned-new
+
 // <string>
 
 // size_type max_size() const; // constexpr since C++20

diff  --git a/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp
index 8e5919539d94e..5e6cb68fd9a42 100644
--- a/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.capacity/over_max_size.pass.cpp
@@ -8,13 +8,8 @@
 
 // UNSUPPORTED: no-exceptions
 
-// This test fails when using a built library that does not contain
-// 15860446a8c3, which changed the return value of max_size(). Without
-// that change, the built library believes the max size to be one greater
-// than it really is, and we fail to throw `length_error` from `string::resize()`,
-// which is explicitly instantiated in the built library.
-//
-// XFAIL: using-built-library-before-llvm-21
+// The allocations are done in the dylib, so we need to use an up-to-date one
+// XFAIL: using-built-library-before-llvm-23
 
 // <string>
 


        


More information about the libcxx-commits mailing list