From: Matthias Kretz <[email protected]>

The previous commit on [simd.bit] was incomplete and not fully tested.
Now, the functions actually implement the constraints as given in the
standard.
The functions returning signed integer types, require an explicit type
cast, since the generator constructor only accepts value-preserving
conversions.

libstdc++-v3/ChangeLog:

        * include/bits/simd_bit.h (byteswap): Fix constraint. Use
        trivial implementation for 1 byte types.
        (bit_ceil, bit_floor, has_single_bit, rotl, rotr, bit_width)
        (countl_zero, countl_one, countr_zero, countr_one, popcount):
        Require unsigned integer types.
        (rotl, rotr): Require integral matching types for the second
        argument.
        (bit_width, countl_zero, countl_one, countr_zero, countr_one)
        (popcount): Fix generator return type to use a signed integer
        type.
        * testsuite/std/simd/simd_bit.cc: Test constraints. Simplify
        construction of expected values.
        Add tests for bit_width, countl_zero, countl_one, countr_zero,
        countr_one, and popcount.
        * testsuite/std/simd/test_setup.h (any_type_of): New.

Signed-off-by: Matthias Kretz <[email protected]>
---
 libstdc++-v3/include/bits/simd_bit.h         |  57 +++++++--
 libstdc++-v3/testsuite/std/simd/simd_bit.cc  | 126 +++++++++++++++----
 libstdc++-v3/testsuite/std/simd/test_setup.h |   3 +
 3 files changed, 150 insertions(+), 36 deletions(-)

diff --git a/libstdc++-v3/include/bits/simd_bit.h 
b/libstdc++-v3/include/bits/simd_bit.h
index 543f0c65c954..dd81c5d6c586 100644
--- a/libstdc++-v3/include/bits/simd_bit.h
+++ b/libstdc++-v3/include/bits/simd_bit.h
@@ -44,12 +44,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 namespace simd
 {
   template<__simd_vec_type _Vp>
+    requires integral<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr _Vp
     byteswap(const _Vp& __v) noexcept
-    { return _Vp([&](int __i) { return std::byteswap(__v[__i]); }); }
+    {
+      if constexpr (sizeof(typename _Vp::value_type) == 1)
+       return __v;
+      else
+       return _Vp([&](int __i) { return std::byteswap(__v[__i]); });
+    }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr _Vp
     bit_ceil(const _Vp& __v)
@@ -61,98 +68,122 @@ namespace simd
     }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr _Vp
     bit_floor(const _Vp& __v) noexcept
     { return _Vp([&](int __i) { return std::bit_floor(__v[__i]); }); }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr typename _Vp::mask_type
     has_single_bit(const _Vp& __v) noexcept
     { return typename _Vp::mask_type([&](int __i) { return 
std::has_single_bit(__v[__i]); }); }
 
   template<__simd_vec_type _V0, __simd_vec_type _V1>
+    requires __unsigned_integer<typename _V0::value_type>
+      && integral<typename _V1::value_type>
+      && (_V0::size() == _V1::size())
+      && (sizeof(typename _V0::value_type) == sizeof(typename _V1::value_type))
     [[__gnu__::__always_inline__]]
     constexpr _V0
     rotl(const _V0& __v, const _V1& __s) noexcept
     { return _V0([&](int __i) { return std::rotl(__v[__i], __s[__i]); }); }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr _Vp
     rotl(const _Vp& __v, int __s) noexcept
     { return _Vp([&](int __i) { return std::rotl(__v[__i], __s); }); }
 
   template<__simd_vec_type _V0, __simd_vec_type _V1>
+    requires __unsigned_integer<typename _V0::value_type>
+      && integral<typename _V1::value_type>
+      && (_V0::size() == _V1::size())
+      && (sizeof(typename _V0::value_type) == sizeof(typename _V1::value_type))
     [[__gnu__::__always_inline__]]
     constexpr _V0
     rotr(const _V0& __v, const _V1& __s) noexcept
     { return _V0([&](int __i) { return std::rotr(__v[__i], __s[__i]); }); }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr _Vp
     rotr(const _Vp& __v, int __s) noexcept
     { return _Vp([&](int __i) { return std::rotr(__v[__i], __s); }); }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>
     bit_width(const _Vp& __v) noexcept
     {
-      return rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>([&](int 
__i) {
-              return std::bit_width(__v[__i]);
+      using _Ip = make_signed_t<typename _Vp::value_type>;
+      return rebind_t<_Ip, _Vp>([&](int __i) {
+              return static_cast<_Ip>(std::bit_width(__v[__i]));
             });
     }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>
     countl_zero(const _Vp& __v) noexcept
     {
-      return rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>([&](int 
__i) {
-              return std::countl_zero(__v[__i]);
+      using _Ip = make_signed_t<typename _Vp::value_type>;
+      return rebind_t<_Ip, _Vp>([&](int __i) {
+              return static_cast<_Ip>(std::countl_zero(__v[__i]));
             });
     }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>
     countl_one(const _Vp& __v) noexcept
     {
-      return rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>([&](int 
__i) {
-              return std::countl_one(__v[__i]);
+      using _Ip = make_signed_t<typename _Vp::value_type>;
+      return rebind_t<_Ip, _Vp>([&](int __i) {
+              return static_cast<_Ip>(std::countl_one(__v[__i]));
             });
     }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>
     countr_zero(const _Vp& __v) noexcept
     {
-      return rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>([&](int 
__i) {
-              return std::countr_zero(__v[__i]);
+      using _Ip = make_signed_t<typename _Vp::value_type>;
+      return rebind_t<_Ip, _Vp>([&](int __i) {
+              return static_cast<_Ip>(std::countr_zero(__v[__i]));
             });
     }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>
     countr_one(const _Vp& __v) noexcept
     {
-      return rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>([&](int 
__i) {
-              return std::countr_one(__v[__i]);
+      using _Ip = make_signed_t<typename _Vp::value_type>;
+      return rebind_t<_Ip, _Vp>([&](int __i) {
+              return static_cast<_Ip>(std::countr_one(__v[__i]));
             });
     }
 
   template<__simd_vec_type _Vp>
+    requires __unsigned_integer<typename _Vp::value_type>
     [[__gnu__::__always_inline__]]
     constexpr rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>
     popcount(const _Vp& __v) noexcept
     {
-      return rebind_t<make_signed_t<typename _Vp::value_type>, _Vp>([&](int 
__i) {
-              return std::popcount(__v[__i]);
+      using _Ip = make_signed_t<typename _Vp::value_type>;
+      return rebind_t<_Ip, _Vp>([&](int __i) {
+              return static_cast<_Ip>(std::popcount(__v[__i]));
             });
     }
 } // namespace simd
diff --git a/libstdc++-v3/testsuite/std/simd/simd_bit.cc 
b/libstdc++-v3/testsuite/std/simd/simd_bit.cc
index 5d2b77c30865..21c78ae9333f 100644
--- a/libstdc++-v3/testsuite/std/simd/simd_bit.cc
+++ b/libstdc++-v3/testsuite/std/simd/simd_bit.cc
@@ -5,9 +5,32 @@
 #include "test_setup.h"
 #include <climits>
 
+template <typename V>
+  struct CheckInvocable
+  {
+    using T = typename V::value_type;
+    static constexpr bool unsigned_integer
+      = any_type_of<T, unsigned char, unsigned short, unsigned int, unsigned 
long,
+                   unsigned long long>;
+    static_assert(std::integral<T> == requires(V x) { std::byteswap(x); });
+    static_assert(unsigned_integer == requires(V x) { std::bit_ceil(x); });
+    static_assert(unsigned_integer == requires(V x) { std::bit_floor(x); });
+    static_assert(unsigned_integer == requires(V x) { std::has_single_bit(x); 
});
+    static_assert(unsigned_integer == requires(V x, V y) { std::rotl(x, y); });
+    static_assert(unsigned_integer == requires(V x, int y) { std::rotl(x, y); 
});
+    static_assert(unsigned_integer == requires(V x, V y) { std::rotr(x, y); });
+    static_assert(unsigned_integer == requires(V x, int y) { std::rotr(x, y); 
});
+    static_assert(unsigned_integer == requires(V x) { std::bit_width(x); });
+    static_assert(unsigned_integer == requires(V x) { std::countl_zero(x); });
+    static_assert(unsigned_integer == requires(V x) { std::countl_one(x); });
+    static_assert(unsigned_integer == requires(V x) { std::countr_zero(x); });
+    static_assert(unsigned_integer == requires(V x) { std::countr_one(x); });
+    static_assert(unsigned_integer == requires(V x) { std::popcount(x); });
+  };
+
 template <typename V>
   requires std::integral<typename V::value_type>
-  struct Tests<V>
+  struct Tests<V> : CheckInvocable<V>
   {
     using T = typename V::value_type;
     using M = typename V::mask_type;
@@ -41,14 +64,7 @@ template <typename V>
        t.verify_equal(bit_ceil(b), select(b == T(), T(1), b));
        t.verify_equal(std::bit_ceil(a), bit_ceil(a));
        t.verify_equal(std::simd::bit_ceil(a), bit_ceil(a));
-       t.verify_equal(bit_ceil(a),
-                      V([](T i) {
-                        if (i > msb)
-                          i -= msb + 1;
-                        while (!std::has_single_bit(i))
-                          i = (i | (i >> 1)) + 1;
-                        return T(i);
-                      }));
+       t.verify_equal(bit_ceil(a), V([&](int i) { return std::bit_ceil(a[i]); 
}));
       }
     };
 
@@ -59,15 +75,7 @@ template <typename V>
        t.verify_equal(bit_floor(b), b);
        t.verify_equal(std::bit_floor(a), bit_floor(a));
        t.verify_equal(std::simd::bit_floor(a), bit_floor(a));
-       t.verify_equal(bit_floor(a),
-                      V([](T i) -> T {
-                        if (i == 0)
-                          return 0;
-                        int shift = 0;
-                        while ((i >> shift) > 1)
-                          ++shift;
-                        return T(1) << shift;
-                      }));
+       t.verify_equal(bit_floor(a), V([&](int i) { return 
std::bit_floor(a[i]); }));
       }
     };
 
@@ -82,12 +90,19 @@ template <typename V>
       }
     };
 
-    ADD_TEST(RotateLeft, std::__unsigned_integer<T>) {
+    ADD_TEST(FullRotate, std::__unsigned_integer<T>) {
       std::tuple {test_iota<V, 0, 0>},
       [](auto& t, const V a) {
-       t.verify_equal(rotl(a, sizeof(T) * CHAR_BIT), a);
-       t.verify_equal(std::rotl(a, sizeof(T) * CHAR_BIT), a);
-       t.verify_equal(std::simd::rotl(a, sizeof(T) * CHAR_BIT), a);
+       constexpr int digits = std::numeric_limits<T>::digits;
+       template for (int n : {0, digits, 5 * digits})
+         {
+           t.verify_equal(rotl(a, n), a);
+           t.verify_equal(std::rotl(a, n), a);
+           t.verify_equal(std::simd::rotl(a, n), a);
+           t.verify_equal(rotr(a, n), a);
+           t.verify_equal(std::rotr(a, n), a);
+           t.verify_equal(std::simd::rotr(a, n), a);
+         }
       }
     };
 
@@ -114,10 +129,75 @@ template <typename V>
        t.verify_equal(rotr(x, I(sizeof(T) * CHAR_BIT) - vshiftx), refx);
       }
     };
+
+    // The value-type of reference is always going to be 'int', forcing a 
conversion in verify_equal
+    // (unless V::value_type is 'unsigned int'). That's intentional, since we 
thus can find
+    // (hypothetical) cases of value-changing conversions in the 
implementation.
+#define REFERENCE(x, fun) simd::rebind_t<decltype(fun(x[0])), V>([&](int i) { 
return fun(x[i]); })
+
+    ADD_TEST(BitWidth, std::__unsigned_integer<T>) {
+      std::tuple {test_iota<V>, msb - test_iota<V>},
+      [](auto& t, const V x, const V y) {
+       t.verify_equal(std::bit_width(x), REFERENCE(x, std::bit_width));
+       t.verify_equal(simd::bit_width(x), REFERENCE(x, std::bit_width));
+       t.verify_equal(std::bit_width(y), REFERENCE(y, std::bit_width));
+       t.verify_equal(simd::bit_width(y), REFERENCE(y, std::bit_width));
+      }
+    };
+
+    ADD_TEST(CountLZero, std::__unsigned_integer<T>) {
+      std::tuple {test_iota<V>, msb - test_iota<V>},
+      [](auto& t, const V x, const V y) {
+       t.verify_equal(std::countl_zero(x), REFERENCE(x, std::countl_zero));
+       t.verify_equal(simd::countl_zero(x), REFERENCE(x, std::countl_zero));
+       t.verify_equal(std::countl_zero(y), REFERENCE(y, std::countl_zero));
+       t.verify_equal(simd::countl_zero(y), REFERENCE(y, std::countl_zero));
+      }
+    };
+
+    ADD_TEST(CountLOne, std::__unsigned_integer<T>) {
+      std::tuple {test_iota<V>, msb - test_iota<V>},
+      [](auto& t, const V x, const V y) {
+       t.verify_equal(std::countl_one(x), REFERENCE(x, std::countl_one));
+       t.verify_equal(simd::countl_one(x), REFERENCE(x, std::countl_one));
+       t.verify_equal(std::countl_one(y), REFERENCE(y, std::countl_one));
+       t.verify_equal(simd::countl_one(y), REFERENCE(y, std::countl_one));
+      }
+    };
+
+    ADD_TEST(CountRZero, std::__unsigned_integer<T>) {
+      std::tuple {test_iota<V>, msb - test_iota<V>},
+      [](auto& t, const V x, const V y) {
+       t.verify_equal(std::countr_zero(x), REFERENCE(x, std::countr_zero));
+       t.verify_equal(simd::countr_zero(x), REFERENCE(x, std::countr_zero));
+       t.verify_equal(std::countr_zero(y), REFERENCE(y, std::countr_zero));
+       t.verify_equal(simd::countr_zero(y), REFERENCE(y, std::countr_zero));
+      }
+    };
+
+    ADD_TEST(CountROne, std::__unsigned_integer<T>) {
+      std::tuple {test_iota<V>, msb - test_iota<V>},
+      [](auto& t, const V x, const V y) {
+       t.verify_equal(std::countr_one(x), REFERENCE(x, std::countr_one));
+       t.verify_equal(simd::countr_one(x), REFERENCE(x, std::countr_one));
+       t.verify_equal(std::countr_one(y), REFERENCE(y, std::countr_one));
+       t.verify_equal(simd::countr_one(y), REFERENCE(y, std::countr_one));
+      }
+    };
+
+    ADD_TEST(PopCount, std::__unsigned_integer<T>) {
+      std::tuple {test_iota<V>, msb - test_iota<V>},
+      [](auto& t, const V x, const V y) {
+       t.verify_equal(std::popcount(x), REFERENCE(x, std::popcount));
+       t.verify_equal(simd::popcount(x), REFERENCE(x, std::popcount));
+       t.verify_equal(std::popcount(y), REFERENCE(y, std::popcount));
+       t.verify_equal(simd::popcount(y), REFERENCE(y, std::popcount));
+      }
+    };
   };
 
 template <typename V>
-  struct Tests
+  struct Tests : CheckInvocable<V>
   {};
 
 #include "create_tests.h"
diff --git a/libstdc++-v3/testsuite/std/simd/test_setup.h 
b/libstdc++-v3/testsuite/std/simd/test_setup.h
index d562a845a5af..9c4fc6025a30 100644
--- a/libstdc++-v3/testsuite/std/simd/test_setup.h
+++ b/libstdc++-v3/testsuite/std/simd/test_setup.h
@@ -73,6 +73,9 @@ static std::string_view test_name = "unknown";
 
 namespace simd = std::simd;
 
+template <typename T, typename... Us>
+  concept any_type_of = (std::same_as<T, Us> || ...);
+
 template <typename T>
   concept complex_like = std::simd::__complex_like<T>;
 
-- 
2.54.0

Reply via email to