| // { dg-do run { target c++26 } } |
| // { dg-require-effective-target x86 } |
| // { dg-additional-options "-msse2" } |
| |
| #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> : CheckInvocable<V> |
| { |
| using T = typename V::value_type; |
| using M = typename V::mask_type; |
| |
| static constexpr T msb = T(std::make_unsigned_t<T>(1) << (sizeof(T) * CHAR_BIT - 1)); |
| |
| ADD_TEST(Byteswap) { |
| std::tuple {test_iota<V>, V(T(0x01'02'03'04'05'06'07'08LL))}, |
| [](auto& t, const V a, const V b) { |
| if constexpr (sizeof(T) == 1) |
| t.verify_equal(std::byteswap(a), a); |
| else |
| { |
| auto x = std::byteswap(a); |
| for (int i = 0; i < V::size(); ++i) |
| t.verify_equal(x[i], std::byteswap(a[i])); |
| auto y = std::simd::byteswap(b); |
| for (int i = 0; i < V::size(); ++i) |
| t.verify_equal(y[i], std::byteswap(b[i])); |
| } |
| } |
| }; |
| |
| ADD_TEST(BitCeil, std::__unsigned_integer<T>) { |
| std::tuple {test_iota<V, 0, msb < test_iota_max<V> ? msb : test_iota_max<V>>, |
| T(1024), T(msb + 1)}, |
| [](auto& t, const V a, const V b, const V c) { |
| t.verify_precondition_failure("bit_ceil result is not representable", [&] { |
| bit_ceil(c); |
| }); |
| 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([&](int i) { return std::bit_ceil(a[i]); })); |
| } |
| }; |
| |
| ADD_TEST(BitFloor, std::__unsigned_integer<T>) { |
| std::tuple {test_iota<V>, T(1024), T(msb + 1)}, |
| [](auto& t, const V a, const V b, const V c) { |
| t.verify_equal(bit_floor(c), msb); |
| 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([&](int i) { return std::bit_floor(a[i]); })); |
| } |
| }; |
| |
| ADD_TEST(HasSingleBit, std::__unsigned_integer<T>) { |
| std::tuple {test_iota<V>, msb, T(msb + 1)}, |
| [](auto& t, const V a, const V b, const V c) { |
| t.verify(all_of(has_single_bit(b))); |
| t.verify(none_of(has_single_bit(c))); |
| t.verify_equal(std::has_single_bit(a), has_single_bit(a)); |
| t.verify_equal(std::simd::has_single_bit(a), has_single_bit(a)); |
| t.verify_equal(has_single_bit(a), a != T() && a == bit_floor(a)); |
| } |
| }; |
| |
| ADD_TEST(FullRotate, std::__unsigned_integer<T>) { |
| std::tuple {test_iota<V, 0, 0>}, |
| [](auto& t, const V 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); |
| } |
| } |
| }; |
| |
| using I = std::make_signed_t<T>; |
| using IV = std::simd::rebind_t<I, V>; |
| |
| ADD_TEST_N(RotateN, 12, std::__unsigned_integer<T>) { |
| std::tuple {test_iota<V, 0, 0>}, |
| []<int N>(auto& t, const V x) { |
| constexpr int shift = 11 * N; |
| constexpr int rshift = I(sizeof(T) * CHAR_BIT) - shift; |
| const IV vshift = I(shift); |
| const IV vshiftx = vshift ^ IV(x & T(1)); |
| V ref([](T i) -> T { return std::rotl(i, shift); }); |
| V refx([](T i) -> T { return std::rotl(i, shift ^ (i & 1)); }); |
| const V l1 = rotl(x, shift); |
| const V lv = rotl(x, vshift); |
| const V lx = rotl(x, vshiftx); |
| t.verify_equal(l1, ref); |
| t.verify_equal(lv, ref); |
| t.verify_equal(lx, refx); |
| t.verify_equal(rotr(x, rshift), ref); |
| t.verify_equal(rotr(x, I(rshift)), ref); |
| 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 : CheckInvocable<V> |
| {}; |
| |
| #include "create_tests.h" |