On 04/23/2017 06:54 PM, Pedro Alves wrote: > Hi! > > As I had suggested in PR c++/80265, here's a patch that uses > __builtin_constant_p to tell whether we can defer to a constexpr > algorithm, which avoids having to wait for compiler support. > > Unfortunately I ran out of cycles today to run a full bootstrap/regtest > cycle, but constexpr_functions_c++17.cc passes cleanly on > x86_64 GNU/Linux at least. > > If this looks like a reasonable approach, I can maybe try running > full tests on the gcc compile farm next week. > > WDYT?
Reading the patch via the list made me spot something that was incorrect. > > - static char_type* > + static _GLIBCXX17_CONSTEXPR char_type* > assign(char_type* __s, size_t __n, char_type __a) > { > +#if __cplusplus > 201402 > + if (__constant_string_p(__s) This should probably have been __constant_char_array_p, but now that I look again at the paper, I see I got myself confused -- this assign overload is not meant to be constexpr in the first place. So here's an updated patch that drops that bit. (Same testing as before.) >From 71cf9b9a3ea79bd790d792c9a02e5a577f1af156 Mon Sep 17 00:00:00 2001 From: Pedro Alves <pal...@redhat.com> Date: Sun, 23 Apr 2017 14:16:09 +0100 Subject: [PATCH] Finish implementing P0426R1 "Constexpr for std::char_traits" for C++17 As discussed in PR c++/80265 (__builtin_{memcmp,memchr,strlen} are not usable in constexpr functions), use __builtin_constant_p to tell whether we can defer to a constexpr algorithm. I used __always_inline__ just to be thorough. It isn't really really necessary as far as I could determine. Changes like these: if (__n == 0) return 0; - return wmemcmp(__s1, __s2, __n); + else + return wmemcmp(__s1, __s2, __n); are necessary otherwise G++ complains that we're calling a non-constexpr function, which looks like a a manifestation of PR67026 to me. libstdc++-v3: 2017-04-23 Pedro Alves <pal...@redhat.com> * doc/xml/manual/status_cxx2017.xml: Update C++17 constexpr char_traits status. * doc/html/*: Regenerate. * include/bits/char_traits.h (_GLIBCXX_ALWAYS_INLINE): Define if not already defined. (__cpp_lib_constexpr_char_traits): Uncomment. (__constant_string_p, __constant_char_array_p): New. (std::char_traits<char>, std::char_traits<wchar_t>): Add _GLIBCXX17_CONSTEXPR on compare, length and find and use __constant_string_p, __constant_char_array_p and __builtin_constant_p to defer to __gnu_cxx::char_traits at compile time. * testsuite/21_strings/char_traits/requirements/ constexpr_functions_c++17.cc: Uncomment __cpp_lib_constexpr_char_traits tests. Uncomment test_compare<char>, test_length<char>, test_find<char>, test_compare<wchar_t>, test_length<wchar_t> and test_find<wchar_t> static_assert tests. --- libstdc++-v3/doc/xml/manual/status_cxx2017.xml | 4 +- libstdc++-v3/include/bits/char_traits.h | 101 ++++++++++++++++++--- .../requirements/constexpr_functions_c++17.cc | 16 ++-- 3 files changed, 100 insertions(+), 21 deletions(-) diff --git a/libstdc++-v3/doc/xml/manual/status_cxx2017.xml b/libstdc++-v3/doc/xml/manual/status_cxx2017.xml index 0e35f75..fed91f9 100644 --- a/libstdc++-v3/doc/xml/manual/status_cxx2017.xml +++ b/libstdc++-v3/doc/xml/manual/status_cxx2017.xml @@ -489,8 +489,8 @@ Feature-testing recommendations for C++</link>. P0426R1 </link> </entry> - <entry align="center"> 7 (partial) </entry> - <entry><code> ??? </code></entry> + <entry align="center"> 7 </entry> + <entry><code> __cpp_lib_constexpr_char_traits >= 201611 </code></entry> </row> <row> diff --git a/libstdc++-v3/include/bits/char_traits.h b/libstdc++-v3/include/bits/char_traits.h index 75db5b8..3ecc30e 100644 --- a/libstdc++-v3/include/bits/char_traits.h +++ b/libstdc++-v3/include/bits/char_traits.h @@ -40,6 +40,10 @@ #include <bits/postypes.h> // For streampos #include <cwchar> // For WEOF, wmemmove, wmemset, etc. +#ifndef _GLIBCXX_ALWAYS_INLINE +#define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) +#endif + namespace __gnu_cxx _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -139,7 +143,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { return !eq_int_type(__c, eof()) ? __c : to_int_type(char_type()); } }; -// #define __cpp_lib_constexpr_char_traits 201611 +#define __cpp_lib_constexpr_char_traits 201611 template<typename _CharT> _GLIBCXX14_CONSTEXPR int @@ -212,6 +216,42 @@ namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION +#if __cplusplus > 201402 + /** + * @brief Determine whether the characters of a NULL-terminated + * string are known at compile time. + * @param __s The string. + * + * Assumes that _CharT is a built-in character type. + */ + template<typename _CharT> + static _GLIBCXX_ALWAYS_INLINE constexpr bool + __constant_string_p(const _CharT* __s) + { + while (__builtin_constant_p(*__s) && *__s) + __s++; + return __builtin_constant_p(*__s); + } + + /** + * @brief Determine whether the characters of a character array are + * known at compile time. + * @param __a The character array. + * @param __n Number of characters. + * + * Assumes that _CharT is a built-in character type. + */ + template<typename _CharT> + static _GLIBCXX_ALWAYS_INLINE constexpr bool + __constant_char_array_p(const _CharT* __a, size_t __n) + { + size_t __i = 0; + while (__builtin_constant_p(__a[__i]) && __i < __n) + __i++; + return __i == __n; + } +#endif + // 21.1 /** * @brief Basis for explicit traits specializations. @@ -256,21 +296,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION < static_cast<unsigned char>(__c2)); } - static /* _GLIBCXX17_CONSTEXPR */ int + static _GLIBCXX17_CONSTEXPR int compare(const char_type* __s1, const char_type* __s2, size_t __n) { +#if __cplusplus > 201402 + if (__builtin_constant_p(__n) + && __constant_char_array_p(__s1, __n) + && __constant_char_array_p(__s2, __n)) + return __gnu_cxx::char_traits<char_type>::compare(__s1, __s2, __n); +#endif if (__n == 0) return 0; return __builtin_memcmp(__s1, __s2, __n); } - static /* _GLIBCXX17_CONSTEXPR */ size_t + static _GLIBCXX17_CONSTEXPR size_t length(const char_type* __s) - { return __builtin_strlen(__s); } + { +#if __cplusplus > 201402 + if (__constant_string_p(__s)) + return __gnu_cxx::char_traits<char_type>::length(__s); +#endif + return __builtin_strlen(__s); + } - static /* _GLIBCXX17_CONSTEXPR */ const char_type* + static _GLIBCXX17_CONSTEXPR const char_type* find(const char_type* __s, size_t __n, const char_type& __a) { +#if __cplusplus > 201402 + if (__builtin_constant_p(__n) + && __builtin_constant_p(__a) + && __constant_char_array_p(__s, __n)) + return __gnu_cxx::char_traits<char_type>::find(__s, __n, __a); +#endif if (__n == 0) return 0; return static_cast<const char_type*>(__builtin_memchr(__s, __a, __n)); @@ -347,24 +405,45 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION lt(const char_type& __c1, const char_type& __c2) _GLIBCXX_NOEXCEPT { return __c1 < __c2; } - static /* _GLIBCXX17_CONSTEXPR */ int + static _GLIBCXX17_CONSTEXPR int compare(const char_type* __s1, const char_type* __s2, size_t __n) { +#if __cplusplus > 201402 + if (__builtin_constant_p(__n) + && __constant_char_array_p(__s1, __n) + && __constant_char_array_p(__s2, __n)) + return __gnu_cxx::char_traits<char_type>::compare(__s1, __s2, __n); +#endif if (__n == 0) return 0; - return wmemcmp(__s1, __s2, __n); + else + return wmemcmp(__s1, __s2, __n); } - static /* _GLIBCXX17_CONSTEXPR */ size_t + static _GLIBCXX17_CONSTEXPR size_t length(const char_type* __s) - { return wcslen(__s); } + { +#if __cplusplus > 201402 + if (__constant_string_p(__s)) + return __gnu_cxx::char_traits<char_type>::length(__s); + else +#endif + return wcslen(__s); + } - static /* _GLIBCXX17_CONSTEXPR */ const char_type* + static _GLIBCXX17_CONSTEXPR const char_type* find(const char_type* __s, size_t __n, const char_type& __a) { +#if __cplusplus > 201402 + if (__builtin_constant_p(__n) + && __builtin_constant_p(__a) + && __constant_char_array_p(__s, __n)) + return __gnu_cxx::char_traits<char_type>::find(__s, __n, __a); +#endif if (__n == 0) return 0; - return wmemchr(__s, __a, __n); + else + return wmemchr(__s, __a, __n); } static char_type* diff --git a/libstdc++-v3/testsuite/21_strings/char_traits/requirements/constexpr_functions_c++17.cc b/libstdc++-v3/testsuite/21_strings/char_traits/requirements/constexpr_functions_c++17.cc index 014caa0..efd280f 100644 --- a/libstdc++-v3/testsuite/21_strings/char_traits/requirements/constexpr_functions_c++17.cc +++ b/libstdc++-v3/testsuite/21_strings/char_traits/requirements/constexpr_functions_c++17.cc @@ -74,20 +74,20 @@ template<typename CT> } #ifndef __cpp_lib_constexpr_char_traits -// #error Feature-test macro for constexpr char_traits is missing +# error Feature-test macro for constexpr char_traits is missing #elif __cpp_lib_constexpr_char_traits != 201611 -// #error Feature-test macro for constexpr char_traits has the wrong value +# error Feature-test macro for constexpr char_traits has the wrong value #endif static_assert( test_assign<std::char_traits<char>>() ); -// static_assert( test_compare<std::char_traits<char>>() ); -// static_assert( test_length<std::char_traits<char>>() ); -// static_assert( test_find<std::char_traits<char>>() ); +static_assert( test_compare<std::char_traits<char>>() ); +static_assert( test_length<std::char_traits<char>>() ); +static_assert( test_find<std::char_traits<char>>() ); #ifdef _GLIBCXX_USE_WCHAR_T static_assert( test_assign<std::char_traits<wchar_t>>() ); -// static_assert( test_compare<std::char_traits<wchar_t>>() ); -// static_assert( test_length<std::char_traits<wchar_t>>() ); -// static_assert( test_find<std::char_traits<wchar_t>>() ); +static_assert( test_compare<std::char_traits<wchar_t>>() ); +static_assert( test_length<std::char_traits<wchar_t>>() ); +static_assert( test_find<std::char_traits<wchar_t>>() ); #endif static_assert( test_assign<std::char_traits<char16_t>>() ); static_assert( test_compare<std::char_traits<char16_t>>() ); -- 2.5.5