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 <[email protected]>
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 <[email protected]>
* 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