@@ -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
@@ -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"
@@ -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>;