diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst index 5f43d2f2afe2..ac78563aa738 100644 --- a/libcxx/docs/ReleaseNotes/18.rst +++ b/libcxx/docs/ReleaseNotes/18.rst @@ -133,6 +133,13 @@ ABI Affecting Changes results in an ABI break, however in practice we expect uses of ``std::projected`` in ABI-sensitive places to be extremely rare. Any error resulting from this change should result in a link-time error. +- Under the unstable ABI, the internal alignment requirements for heap allocations + inside ``std::string`` has decreased from 16 to 8 This save memory since string requests fewer additional + bytes than it did previously. However, this also changes the return value of ``std::string::max_size`` + and can cause code compiled against older libc++ versions but linked at runtime to a new version + to throw a different exception when attempting allocations that are too large + (``std::bad_alloc`` vs ``std::length_error``). + Build System Changes -------------------- diff --git a/libcxx/include/__config b/libcxx/include/__config index 55d9f1c73765..65ce6d6a27f8 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -167,6 +167,11 @@ // The implementation moved to the header, but we still export the symbols from // the dylib for backwards compatibility. # define _LIBCPP_ABI_DO_NOT_EXPORT_TO_CHARS_BASE_10 +// Save memory by providing the allocator more freedom to allocate the most +// efficient size class by dropping the alignment requirements for std::string's +// pointer from 16 to 8. This changes the output of std::string::max_size, +// which makes it ABI breaking +# define _LIBCPP_ABI_STRING_8_BYTE_ALIGNMENT # elif _LIBCPP_ABI_VERSION == 1 # if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF)) // Enable compiling copies of now inline methods into the dylib to support diff --git a/libcxx/include/string b/libcxx/include/string index 33e87406a115..3078715e02b3 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -1851,7 +1851,14 @@ private: _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __align_it(size_type __s) _NOEXCEPT {return (__s + (__a-1)) & ~(__a-1);} - enum {__alignment = 16}; + enum { + __alignment = +#ifdef _LIBCPP_ABI_STRING_8_BYTE_ALIGNMENT + 8 +#else + 16 +#endif + }; static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type __recommend(size_type __s) _NOEXCEPT { 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 new file mode 100644 index 000000000000..c7df56c815a8 --- /dev/null +++ b/libcxx/test/libcxx/strings/basic.string/string.capacity/allocation_size.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// This test demonstrates the smaller allocation sizes when the alignment +// requirements of std::string are dropped from 16 to 8. +#include +#include +#include +#include + +#include "test_macros.h" + +// alignment of the string heap buffer is hardcoded to either 16 or 8 + +const std::size_t alignment = +#ifdef _LIBCPP_ABI_STRING_8_BYTE_ALIGNMENT + 8; +#else + 16; +#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()); + 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); + } else { + assert(test_string.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 5af9cab0be4e..a3cb79522f2e 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 @@ -18,7 +18,13 @@ #include "test_macros.h" // alignment of the string heap buffer is hardcoded to 16 -static const std::size_t alignment = 16; + +static const std::size_t alignment = +#ifdef _LIBCPP_ABI_STRING_8_BYTE_ALIGNMENT + 8; +#else + 16; +#endif template TEST_CONSTEXPR_CXX20 void full_size() {