https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105329
Jakub Jelinek <jakub at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |jakub at gcc dot gnu.org --- Comment #14 from Jakub Jelinek <jakub at gcc dot gnu.org> --- Here is an untested patch for the cold part of the function: --- libstdc++-v3/include/bits/basic_string.h.jj 2022-01-21 22:48:42.220261654 +0100 +++ libstdc++-v3/include/bits/basic_string.h 2022-05-02 15:51:42.381923962 +0200 @@ -2504,6 +2504,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _M_replace_aux(size_type __pos1, size_type __n1, size_type __n2, _CharT __c); + __attribute__((__noinline__, __noclone__, __cold__)) void + _M_replace_cold(pointer __p, size_type __len1, const _CharT* __s, + const size_type __len2, const size_type __how_much); + _GLIBCXX20_CONSTEXPR basic_string& _M_replace(size_type __pos, size_type __len1, const _CharT* __s, --- libstdc++-v3/include/bits/basic_string.tcc.jj 2022-01-11 22:31:41.482757256 +0100 +++ libstdc++-v3/include/bits/basic_string.tcc 2022-05-02 15:51:38.630975348 +0200 @@ -471,6 +471,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename _CharT, typename _Traits, typename _Alloc> + __attribute__((__noinline__, __noclone__, __cold__)) void + basic_string<_CharT, _Traits, _Alloc>:: + _M_replace_cold(pointer __p, size_type __len1, const _CharT* __s, + const size_type __len2, const size_type __how_much) + { + // Work in-place. + if (__len2 && __len2 <= __len1) + this->_S_move(__p, __s, __len2); + if (__how_much && __len1 != __len2) + this->_S_move(__p + __len2, __p + __len1, __how_much); + if (__len2 > __len1) + { + if (__s + __len2 <= __p + __len1) + this->_S_move(__p, __s, __len2); + else if (__s >= __p + __len1) + { + // Hint to middle end that __p and __s overlap + // (PR 98465). + const size_type __poff = (__s - __p) + (__len2 - __len1); + this->_S_copy(__p, __p + __poff, __len2); + } + else + { + const size_type __nleft = (__p + __len1) - __s; + this->_S_move(__p, __s, __nleft); + this->_S_copy(__p + __nleft, __p + __len2, __len2 - __nleft); + } + } + } + + template<typename _CharT, typename _Traits, typename _Alloc> _GLIBCXX20_CONSTEXPR basic_string<_CharT, _Traits, _Alloc>& basic_string<_CharT, _Traits, _Alloc>:: @@ -500,7 +531,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } else #endif - if (_M_disjunct(__s)) + if (__builtin_expect(_M_disjunct(__s),true)) { if (__how_much && __len1 != __len2) this->_S_move(__p + __len2, __p + __len1, __how_much); @@ -508,32 +539,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION this->_S_copy(__p, __s, __len2); } else - { - // Work in-place. - if (__len2 && __len2 <= __len1) - this->_S_move(__p, __s, __len2); - if (__how_much && __len1 != __len2) - this->_S_move(__p + __len2, __p + __len1, __how_much); - if (__len2 > __len1) - { - if (__s + __len2 <= __p + __len1) - this->_S_move(__p, __s, __len2); - else if (__s >= __p + __len1) - { - // Hint to middle end that __p and __s overlap - // (PR 98465). - const size_type __poff = (__s - __p) + (__len2 - __len1); - this->_S_copy(__p, __p + __poff, __len2); - } - else - { - const size_type __nleft = (__p + __len1) - __s; - this->_S_move(__p, __s, __nleft); - this->_S_copy(__p + __nleft, __p + __len2, - __len2 - __nleft); - } - } - } + _M_replace_cold(__p, __len1, __s, __len2, __how_much); } else this->_M_mutate(__pos, __len1, __s, __len2); Note, on the #c0 testcase alone it actually results in larger generated code (unless we arrange for it to be exported from libstdc++, for which it is quite late for 12.1 right now), but say on: #include <string> void f (std::string& s) { s = "5"; } void g (std::string& s) { s = "56"; } void h (std::string& s) { s = "6"; } it is already smaller (note, obviously it doesn't need to be in the same TU).