https://gcc.gnu.org/g:1046c32de4956c3d706a2ff8683582fd21b8f360
commit r15-5735-g1046c32de4956c3d706a2ff8683582fd21b8f360 Author: Jan Hubicka <hubi...@ucw.cz> Date: Wed Nov 27 23:52:37 2024 +0100 optimize basic_string Add __builtin_unreachable conditionls to declare value ranges of basic_string::length(). FIx max_size() to return actual max size using logic similar to std::vector. Aviod use of size() in empty() to save some compile time overhead. As disucced, max_size() change is technically ABI breaking, but hopefully this does not really matter in practice. Change of length() breaks empty-loop testcase where we now optimize the loop only after inlining, so template is updated to check cddce3 instead of cddce2. This is PR117764. With these chages we now optimize out unused strings as tested in string-1.C libstdc++-v3/ChangeLog: * include/bits/basic_string.h (basic_string::size(), basic_string::length(), basic_string::capacity()): Add __builtin_unreachable to declare value ranges. (basic_string::empty()): Implement directly (basic_string::max_size()): Account correctly the terminating 0 and limits implied by ptrdiff_t. gcc/testsuite/ChangeLog: * g++.dg/tree-ssa/empty-loop.C: xfail optimization at cddce2 and check it happens at cddce3. * g++.dg/tree-ssa/string-1.C: New test. Diff: --- gcc/testsuite/g++.dg/tree-ssa/empty-loop.C | 5 ++++- gcc/testsuite/g++.dg/tree-ssa/string-1.C | 9 +++++++++ libstdc++-v3/include/bits/basic_string.h | 25 +++++++++++++++++++------ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/gcc/testsuite/g++.dg/tree-ssa/empty-loop.C b/gcc/testsuite/g++.dg/tree-ssa/empty-loop.C index ed4a603bf5b3..b7e7e27cc042 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/empty-loop.C +++ b/gcc/testsuite/g++.dg/tree-ssa/empty-loop.C @@ -30,5 +30,8 @@ int foo (vector<string> &v, list<string> &l, set<string> &s, map<int, string> &m return 0; } -/* { dg-final { scan-tree-dump-not "if" "cddce2"} } */ +/* Adding __builtin_unreachable to std::string::size() prevents cddce2 from + eliminating the loop early, see PR117764. */ +/* { dg-final { scan-tree-dump-not "if" "cddce2" { xfail *-*-* } } } */ +/* { dg-final { scan-tree-dump-not "if" "cddce3"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/string-1.C b/gcc/testsuite/g++.dg/tree-ssa/string-1.C new file mode 100644 index 000000000000..d38c23a7628b --- /dev/null +++ b/gcc/testsuite/g++.dg/tree-ssa/string-1.C @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -std=c++20 -fdump-tree-optimized" } */ +#include <string> +std::string +test (std::string &a) +{ + return a; +} +/* { dg-final { scan-tree-dump-not "throw" "optimized" } } */ diff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h index f5b320099b17..17b973c8b45c 100644 --- a/libstdc++-v3/include/bits/basic_string.h +++ b/libstdc++-v3/include/bits/basic_string.h @@ -1079,20 +1079,30 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR size_type size() const _GLIBCXX_NOEXCEPT - { return _M_string_length; } + { + size_type __sz = _M_string_length; + if (__sz > max_size ()) + __builtin_unreachable (); + return __sz; + } /// Returns the number of characters in the string, not including any /// null-termination. _GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR size_type length() const _GLIBCXX_NOEXCEPT - { return _M_string_length; } + { return size(); } /// Returns the size() of the largest possible %string. _GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR size_type max_size() const _GLIBCXX_NOEXCEPT - { return (_Alloc_traits::max_size(_M_get_allocator()) - 1) / 2; } + { + const size_t __diffmax + = __gnu_cxx::__numeric_traits<ptrdiff_t>::__max / sizeof(_CharT); + const size_t __allocmax = _Alloc_traits::max_size(_M_get_allocator()); + return (std::min)(__diffmax, __allocmax) - 1; + } /** * @brief Resizes the %string to the specified number of characters. @@ -1184,8 +1194,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 size_type capacity() const _GLIBCXX_NOEXCEPT { - return _M_is_local() ? size_type(_S_local_capacity) - : _M_allocated_capacity; + size_t __sz = _M_is_local() ? size_type(_S_local_capacity) + : _M_allocated_capacity; + if (__sz < _S_local_capacity || __sz > max_size ()) + __builtin_unreachable (); + return __sz; } /** @@ -1234,7 +1247,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR bool empty() const _GLIBCXX_NOEXCEPT - { return this->size() == 0; } + { return _M_string_length == 0; } // Element access: /**