[PATCH] libstdc++: Use constexpr-if for std::basic_string::_S_copy_chars
Tomasz Kaminski
tkaminsk@redhat.com
Fri Apr 11 11:20:45 GMT 2025
On Fri, Apr 11, 2025 at 12:52 PM Jonathan Wakely <jwakely@redhat.com> wrote:
> On 11/04/25 09:49 +0200, Tomasz Kaminski wrote:
> >On Thu, Apr 10, 2025 at 6:29 PM Jonathan Wakely <jwakely@redhat.com>
> wrote:
> >
> >> For C++11 and later we can remvoe four overloads of _S_copy_chars and
> >> use constexpr-if in the generic _S_copy_chars. This simplifies overload
> >> resolution for _S_copy_chars, and also means that we optimize for other
> >> iterators such as std::vector<char>::iterator.
> >>
> >> We still need all the _S_copy_chars overloads to be part of the explicit
> >> instantiation definition, so make them depend on the macro that is
> >> defined by src/c++11/string-inst.cc for that purpose.
> >>
> >> libstdc++-v3/ChangeLog:
> >>
> >> * include/bits/basic_string.h (_S_copy_chars): Replace overloads
> >> with constexpr-if and extend optimization to all contiguous
> >> iterators.
> >> * src/c++11/string-inst.cc: Extend comment.
> >> ---
> >>
> >> Tested x86_64-linux.
> >>
> >LGTM with one clarification question.
> >
> >>
> >> libstdc++-v3/include/bits/basic_string.h | 31 ++++++++++++++++++------
> >> libstdc++-v3/src/c++11/string-inst.cc | 3 ++-
> >> 2 files changed, 25 insertions(+), 9 deletions(-)
> >>
> >> diff --git a/libstdc++-v3/include/bits/basic_string.h
> >> b/libstdc++-v3/include/bits/basic_string.h
> >> index 886e7e6b19e..067c7915c76 100644
> >> --- a/libstdc++-v3/include/bits/basic_string.h
> >> +++ b/libstdc++-v3/include/bits/basic_string.h
> >> @@ -468,6 +468,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> >> traits_type::assign(__d, __n, __c);
> >> }
> >>
> >> +#pragma GCC diagnostic push
> >> +#pragma GCC diagnostic ignored "-Wc++17-extensions"
> >> // _S_copy_chars is a separate template to permit specialization
> >> // to optimize for the common case of pointers as iterators.
> >> template<class _Iterator>
> >> @@ -475,31 +477,44 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
> >> static void
> >> _S_copy_chars(_CharT* __p, _Iterator __k1, _Iterator __k2)
> >> {
> >> +#if __cplusplus >= 201103L
> >> + using _IterBase = decltype(std::__niter_base(__k1));
> >> + if constexpr (__or_<is_same<_IterBase, _CharT*>,
> >> + is_same<_IterBase, const _CharT*>>::value)
> >> + _S_copy(__p, std::__niter_base(__k1), __k2 - __k1);
> >> +#if __cpp_lib_concepts
> >> + else if constexpr (contiguous_iterator<_Iterator>
> >> + && is_same_v<iter_value_t<_Iterator>,
> >> _CharT>)
> >> + {
> >> + const auto __d = __k2 - __k1;
> >> + (void) (__k1 + __d); // See P3349R1
> >> + _S_copy(__p, std::to_address(__k1),
> >> static_cast<size_type>(__d));
> >> + }
> >> +#endif
> >> + else
> >> +#endif
> >> for (; __k1 != __k2; ++__k1, (void)++__p)
> >> traits_type::assign(*__p, *__k1); // These types are off.
> >> }
> >> +#pragma GCC diagnostic pop
> >>
> >> - _GLIBCXX20_CONSTEXPR
> >> +#if __cplusplus < 201103L || defined
> >> _GLIBCXX_DEFINING_STRING_INSTANTIATIONS
> >> static void
> >> - _S_copy_chars(_CharT* __p, iterator __k1, iterator __k2)
> >> _GLIBCXX_NOEXCEPT
> >>
> >The `_GLIBCXX_DEFINING_STRING_INSTANTIATIONS` seem to be used for
> >c++11/string-inst.cc, in
> >which case the noexcept was meaningful.
>
> Is it?
>
I meant in a sense that `_GLIBCXX_NOEXCEPT` expanded to something. Nothing
beyond that.
>
> >The macro is not used in
> >c++17/string-inst.cc file, so noexcept
>
> The src/c++17/string-inst.cc file only instantiates a handful of
> member functions, and none of them uses _S_copy_chars. So I don't
> think it matters for src/c++17/string-inst.cc at all.
>
> >would be part of the function signature, so I do not think we change
> >emitted symbols. Is this the reason
> >why did you remove it?
>
> I removed it because those overloads of _S_copy_chars are only visible
> in the header for C++98, where _GLIBCXX_NOEXCEPT expands to nothing.
>
> You're correct that in src/c++11/string-inst.cc those overloads are
> instantiated for C++11 (using -std=gnu++11), where it would expand to
> 'noexcept'. But I don't think it's meaningful whether it's present or
> not.
>
> In C++11 the noexcept isn't part of the type, and it never affects the
> mangled name even in C++17. For these _S_copy_chars overloads, the
> compiler can easily see that they don't throw, as they only call
> std::char_traits<char>::assign or std::char_traits<char>::copy (or the
> wchar_t equivalents) and those are noexcept. So for the explicit
> instantiation definitions, the compiler knows they can't throw, and so
> it never needs to use a noexcept landing pad to terminate if an
> exception happens. So noexcept on the definition does nothing. Those
> functions can't throw, and we don't need (or want) extra code emitted
> to enforce the noexcept by terminating.
>
> So the noexcept is only meaningful at the call site, so that callers
> can see that those functions don't throw (and don't need their own
> exception handling code). The call sites in string-inst.cc should be
> able to see that they don't throw (they're compiled with -O2, and I
> checked on x86_64 and there are no calls to _S_copy_chars left in the
> assembly code). The call sites in user code can only see those
> overloads for C++98, where _GLIBCXX_NOEXCEPT expands to nothing.
>
> So I don't think the 'noexcept' is ever meaningful.
>
>
> >> + _S_copy_chars(_CharT* __p, iterator __k1, iterator __k2)
> >> { _S_copy_chars(__p, __k1.base(), __k2.base()); }
> >>
> >> - _GLIBCXX20_CONSTEXPR
> >> static void
> >> _S_copy_chars(_CharT* __p, const_iterator __k1, const_iterator
> __k2)
> >> - _GLIBCXX_NOEXCEPT
> >> { _S_copy_chars(__p, __k1.base(), __k2.base()); }
> >>
> >> - _GLIBCXX20_CONSTEXPR
> >> static void
> >> - _S_copy_chars(_CharT* __p, _CharT* __k1, _CharT* __k2)
> >> _GLIBCXX_NOEXCEPT
> >> + _S_copy_chars(_CharT* __p, _CharT* __k1, _CharT* __k2)
> >> { _S_copy(__p, __k1, __k2 - __k1); }
> >>
> >> - _GLIBCXX20_CONSTEXPR
> >> static void
> >> _S_copy_chars(_CharT* __p, const _CharT* __k1, const _CharT*
> __k2)
> >> - _GLIBCXX_NOEXCEPT
> >> { _S_copy(__p, __k1, __k2 - __k1); }
> >> +#endif
> >>
> >> _GLIBCXX20_CONSTEXPR
> >> static int
> >> diff --git a/libstdc++-v3/src/c++11/string-inst.cc
> >> b/libstdc++-v3/src/c++11/string-inst.cc
> >> index c4864794d40..34df909b31a 100644
> >> --- a/libstdc++-v3/src/c++11/string-inst.cc
> >> +++ b/libstdc++-v3/src/c++11/string-inst.cc
> >> @@ -40,7 +40,8 @@
> >> // replaced by constrained function templates, so that we instantiate
> the
> >> // pre-C++17 definitions.
> >> // This also causes the instantiation of the non-standard C++0x-era
> >> -// insert(iterator, initializer_list<C>) overload, see PR
> libstdc++/83328
> >> +// insert(iterator, initializer_list<C>) overload, see PR
> libstdc++/83328,
> >> +// and overloads of _S_copy_chars for string iterators and pointers.
> >> #define _GLIBCXX_DEFINING_STRING_INSTANTIATIONS 1
> >>
> >> #include <string>
> >> --
> >> 2.49.0
> >>
> >>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250411/4142d77f/attachment.htm>
More information about the Libstdc++
mailing list