Hi! Jakub recently implemented most of P3793R2; this patch completes it by implementing the [simd] support and defining the FTM.
The implementation is straightforward within the existing SIMD framework. Built and tested on x86_64-linux. OK for trunk? Thanks, Yuao
From 56784f44a945cf887203da682303f0bb0318817b Mon Sep 17 00:00:00 2001 From: Yuao Ma <[email protected]> Date: Sat, 12 Sep 2026 17:14:50 +0800 Subject: [PATCH] libstdc++: add [simd] functionality to std::shl/std::shr libstdc++-v3/ChangeLog: * include/bits/simd_bit.h (simd::shl, simd::shr): Add. * include/bits/version.def: Add simd_bitops. * include/bits/version.h: Regenerate. * include/std/simd: Define __glibcxx_want_simd_bitops. * testsuite/std/simd/simd_bit.cc: Add tests for shl/shr. --- libstdc++-v3/include/bits/simd_bit.h | 34 ++++++++++ libstdc++-v3/include/bits/version.def | 11 +++ libstdc++-v3/include/bits/version.h | 9 +++ libstdc++-v3/include/std/simd | 1 + libstdc++-v3/testsuite/std/simd/simd_bit.cc | 74 ++++++++++++++++++++- 5 files changed, 126 insertions(+), 3 deletions(-) diff --git a/libstdc++-v3/include/bits/simd_bit.h b/libstdc++-v3/include/bits/simd_bit.h index f3de34cc639..969645ffd9f 100644 --- a/libstdc++-v3/include/bits/simd_bit.h +++ b/libstdc++-v3/include/bits/simd_bit.h @@ -77,6 +77,36 @@ namespace simd has_single_bit(const _Vp& __v) noexcept { return typename _Vp::mask_type([&](int __i) { return std::has_single_bit(__v[__i]); }); } +#ifdef __glibcxx_simd_bitops + template<__simd_integral _VX, __simd_integral _VS> + requires (_VX::size() == _VS::size()) + && (sizeof(typename _VX::value_type) == sizeof(typename _VS::value_type)) + [[__gnu__::__always_inline__]] + constexpr _VX + shl(const _VX& __v, const _VS& __s) noexcept + { return _VX([&](int __i) { return std::shl(__v[__i], __s[__i]); }); } + + template<__simd_integral _Vp, __signed_or_unsigned_integer _Sp> + [[__gnu__::__always_inline__]] + constexpr _Vp + shl(const _Vp& __v, _Sp __s) noexcept + { return _Vp([&](int __i) { return std::shl(__v[__i], __s); }); } + + template<__simd_integral _VX, __simd_integral _VS> + requires (_VX::size() == _VS::size()) + && (sizeof(typename _VX::value_type) == sizeof(typename _VS::value_type)) + [[__gnu__::__always_inline__]] + constexpr _VX + shr(const _VX& __v, const _VS& __s) noexcept + { return _VX([&](int __i) { return std::shr(__v[__i], __s[__i]); }); } + + template<__simd_integral _Vp, __signed_or_unsigned_integer _Sp> + [[__gnu__::__always_inline__]] + constexpr _Vp + shr(const _Vp& __v, _Sp __s) noexcept + { return _Vp([&](int __i) { return std::shr(__v[__i], __s); }); } +#endif + template<__simd_unsigned_integer _V0, __simd_integral _V1> requires (_V0::size() == _V1::size()) && (sizeof(typename _V0::value_type) == sizeof(typename _V1::value_type)) @@ -176,6 +206,10 @@ namespace simd using simd::bit_ceil; using simd::bit_floor; using simd::has_single_bit; +#ifdef __glibcxx_simd_bitops + using simd::shl; + using simd::shr; +#endif using simd::rotl; using simd::rotr; using simd::bit_width; diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 36f8c9c31cf..9ed2a4a02e1 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -2482,6 +2482,17 @@ ftms = { }; }; +ftms = { + name = simd_bitops; + values = { + no_stdname = true; // TODO: change once complete + v = 202607; + cxxmin = 29; + extra_cond = "defined __glibcxx_simd"; + hosted = yes; + }; +}; + ftms = { name = simd_complex; values = { diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index e79ea5043d7..8799771dfd9 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2735,6 +2735,15 @@ #endif /* !defined(__cpp_lib_simd) */ #undef __glibcxx_want_simd +#if !defined(__cpp_lib_simd_bitops) +# if (__cplusplus > 202603L) && _GLIBCXX_HOSTED && (defined __glibcxx_simd) +# define __glibcxx_simd_bitops 202607L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_simd_bitops) +# endif +# endif +#endif /* !defined(__cpp_lib_simd_bitops) */ +#undef __glibcxx_want_simd_bitops + #if !defined(__cpp_lib_simd_complex) # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED && (defined __glibcxx_simd) # define __glibcxx_simd_complex 202502L diff --git a/libstdc++-v3/include/std/simd b/libstdc++-v3/include/std/simd index 0523fce3baf..f63cd63fb93 100644 --- a/libstdc++-v3/include/std/simd +++ b/libstdc++-v3/include/std/simd @@ -34,6 +34,7 @@ #endif #define __glibcxx_want_simd +#define __glibcxx_want_simd_bitops #define __glibcxx_want_simd_complex #include <bits/version.h> diff --git a/libstdc++-v3/testsuite/std/simd/simd_bit.cc b/libstdc++-v3/testsuite/std/simd/simd_bit.cc index 21c78ae9333..d8dcac8957b 100644 --- a/libstdc++-v3/testsuite/std/simd/simd_bit.cc +++ b/libstdc++-v3/testsuite/std/simd/simd_bit.cc @@ -16,6 +16,23 @@ template <typename V> 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); }); +#if __cplusplus > 202603L + static_assert(std::integral<T> == requires(V x, V y) { std::shl(x, y); }); + static_assert(std::integral<T> == requires(V x, int y) { std::shl(x, y); }); + static_assert(std::integral<T> == requires(V x, V y) { std::shr(x, y); }); + static_assert(std::integral<T> == requires(V x, int y) { std::shr(x, y); }); + static_assert(!requires(V x, bool y) { std::shl(x, y); }); + static_assert(!requires(V x, bool y) { std::shr(x, y); }); + static_assert(!requires(V x, float y) { std::shl(x, y); }); + static_assert(!requires(V x, float y) { std::shr(x, y); }); + using OtherSize = simd::resize_t<V::size() + 1, V>; + using OtherWidth = simd::rebind_t< + std::conditional_t<sizeof(T) == 1, unsigned short, unsigned char>, V>; + static_assert(!requires(V x, OtherSize y) { std::shl(x, y); }); + static_assert(!requires(V x, OtherSize y) { std::shr(x, y); }); + static_assert(!requires(V x, OtherWidth y) { std::shl(x, y); }); + static_assert(!requires(V x, OtherWidth y) { std::shr(x, y); }); +#endif 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); }); @@ -34,6 +51,10 @@ template <typename V> { using T = typename V::value_type; using M = typename V::mask_type; + using I = std::make_signed_t<T>; + using IV = std::simd::rebind_t<I, V>; + using U = std::make_unsigned_t<T>; + using UV = std::simd::rebind_t<U, V>; static constexpr T msb = T(std::make_unsigned_t<T>(1) << (sizeof(T) * CHAR_BIT - 1)); @@ -90,6 +111,56 @@ template <typename V> } }; +#if __cplusplus > 202603L + ADD_TEST(Shifts) { + std::tuple {test_iota<V>, V(msb)}, + [](auto& t, const V a, const V b) { + constexpr int width = sizeof(T) * CHAR_BIT; + const long long shifts[] = { + 0, 1, width - 1, width, width + 1, -1, 1 - width, -width, -width - 1, + std::numeric_limits<I>::min(), std::numeric_limits<I>::max(), + LLONG_MIN, -0x1'0000'0001LL, 0x1'0000'0001LL, LLONG_MAX + }; + for (V v : {a, ~a, b, ~b}) + template for (auto x : {IV(v), UV(v)}) + { + using W = decltype(x); + for (long long n : shifts) + { + if !consteval + { + n = make_value_unknown(n); + } + const IV si([&](int i) { return I(I(n) ^ I(i)); }); + const UV su([&](int i) { return U(U(n) ^ U(i)); }); + template for (auto s : { + int(n), I(n), U(n), n, static_cast<unsigned long long>(n), si, su}) + { + static_assert(std::same_as<decltype(std::shl(x, s)), W>); + static_assert(std::same_as<decltype(std::shr(x, s)), W>); + static_assert(noexcept(std::shl(x, s))); + static_assert(noexcept(std::shr(x, s))); + const auto count = [&](int i) { + if constexpr (std::integral<decltype(s)>) + return s; + else + return s[i]; + }; + const W left([&](int i) { return std::shl(x[i], count(i)); }); + const W right([&](int i) { return std::shr(x[i], count(i)); }); + t.verify_equal(std::shl(x, s), left); + t.verify_equal(std::simd::shl(x, s), left); + t.verify_equal(shl(x, s), left); + t.verify_equal(std::shr(x, s), right); + t.verify_equal(std::simd::shr(x, s), right); + t.verify_equal(shr(x, s), right); + } + } + } + } + }; +#endif + ADD_TEST(FullRotate, std::__unsigned_integer<T>) { std::tuple {test_iota<V, 0, 0>}, [](auto& t, const V a) { @@ -106,9 +177,6 @@ template <typename V> } }; - 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) { -- 2.54.0
