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

Reply via email to