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