libstdc++: Fix find_last_set(simd_mask) to ignore padding bits

Message ID 2402789.jZfb76A358@centauriprime
State Committed
Commit 1340ddea0158de3f49aeb75b4013e5fc313ff6f4
Headers
Series libstdc++: Fix find_last_set(simd_mask) to ignore padding bits |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed

Commit Message

Matthias Kretz June 18, 2024, 6:22 a.m. UTC
  Tested on x86_64-linux-gnu (also -m32 and -mx32), aarch64-linux-gnu, and arm-
linux-gnueabi(hf).

OK for trunk and backports? OK, to go for GCC 11.5 as early as possible?

----------------------- 8< -----------------------

With the change to the AVX512 find_last_set implementation, the change
to AVX512 operator!= is unnecessary. However, the latter was not
producing optimal code and unnecessarily set the padding bits. In
theory, the compiler could determine that with the new !=
implementation, the bit operation for clearing the padding bits is a
no-op and can be elided.

Signed-off-by: Matthias Kretz <m.kretz@gsi.de>

libstdc++-v3/ChangeLog:

	PR libstdc++/115454
	* include/experimental/bits/simd_x86.h (_S_not_equal_to): Use
	neq comparison instead of bitwise negation after eq.
	(_S_find_last_set): Clear unused high bits before computing
	bit_width.
	* testsuite/experimental/simd/pr115454_find_last_set.cc: New
	test.
---
 .../include/experimental/bits/simd_x86.h      | 26 +++++-----
 .../simd/pr115454_find_last_set.cc            | 49 +++++++++++++++++++
 2 files changed, 62 insertions(+), 13 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/experimental/simd/
pr115454_find_last_set.cc


--
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────
  

Comments

Matthias Kretz June 20, 2024, 7:28 a.m. UTC | #1
Ping.

I fear that time to make GCC 11.5 is running out.

-Matthias

On Tuesday, 18 June 2024 08:22:13 GMT+2 Matthias Kretz wrote:
> Tested on x86_64-linux-gnu (also -m32 and -mx32), aarch64-linux-gnu, and
> arm- linux-gnueabi(hf).
> 
> OK for trunk and backports? OK, to go for GCC 11.5 as early as possible?
> 
> ----------------------- 8< -----------------------
> 
> With the change to the AVX512 find_last_set implementation, the change
> to AVX512 operator!= is unnecessary. However, the latter was not
> producing optimal code and unnecessarily set the padding bits. In
> theory, the compiler could determine that with the new !=
> implementation, the bit operation for clearing the padding bits is a
> no-op and can be elided.
> 
> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
> 
> libstdc++-v3/ChangeLog:
> 
> 	PR libstdc++/115454
> 	* include/experimental/bits/simd_x86.h (_S_not_equal_to): Use
> 	neq comparison instead of bitwise negation after eq.
> 	(_S_find_last_set): Clear unused high bits before computing
> 	bit_width.
> 	* testsuite/experimental/simd/pr115454_find_last_set.cc: New
> 	test.
> ---
>  .../include/experimental/bits/simd_x86.h      | 26 +++++-----
>  .../simd/pr115454_find_last_set.cc            | 49 +++++++++++++++++++
>  2 files changed, 62 insertions(+), 13 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/experimental/simd/
> pr115454_find_last_set.cc
> 
> 
> --
> ──────────────────────────────────────────────────────────────────────────
>  Dr. Matthias Kretz                           https://mattkretz.github.io
>  GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
>  stdₓ::simd
> ──────────────────────────────────────────────────────────────────────────
  
Jonathan Wakely June 20, 2024, 10:27 a.m. UTC | #2
On Tue, 18 Jun 2024 at 07:22, Matthias Kretz <m.kretz@gsi.de> wrote:
>
> Tested on x86_64-linux-gnu (also -m32 and -mx32), aarch64-linux-gnu, and arm-
> linux-gnueabi(hf).
>
> OK for trunk and backports? OK, to go for GCC 11.5 as early as possible?

OK for all branches, thanks.


>
> ----------------------- 8< -----------------------
>
> With the change to the AVX512 find_last_set implementation, the change
> to AVX512 operator!= is unnecessary. However, the latter was not
> producing optimal code and unnecessarily set the padding bits. In
> theory, the compiler could determine that with the new !=
> implementation, the bit operation for clearing the padding bits is a
> no-op and can be elided.
>
> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
>
> libstdc++-v3/ChangeLog:
>
>         PR libstdc++/115454
>         * include/experimental/bits/simd_x86.h (_S_not_equal_to): Use
>         neq comparison instead of bitwise negation after eq.
>         (_S_find_last_set): Clear unused high bits before computing
>         bit_width.
>         * testsuite/experimental/simd/pr115454_find_last_set.cc: New
>         test.
> ---
>  .../include/experimental/bits/simd_x86.h      | 26 +++++-----
>  .../simd/pr115454_find_last_set.cc            | 49 +++++++++++++++++++
>  2 files changed, 62 insertions(+), 13 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/experimental/simd/
> pr115454_find_last_set.cc
>
>
> --
> ──────────────────────────────────────────────────────────────────────────
>  Dr. Matthias Kretz                           https://mattkretz.github.io
>  GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
>  stdₓ::simd
> ──────────────────────────────────────────────────────────────────────────
  

Patch

diff --git a/libstdc++-v3/include/experimental/bits/simd_x86.h b/libstdc++-v3/include/experimental/bits/simd_x86.h
index 517c4b4a5be..8a23aa2082b 100644
--- a/libstdc++-v3/include/experimental/bits/simd_x86.h
+++ b/libstdc++-v3/include/experimental/bits/simd_x86.h
@@ -2339,29 +2339,29 @@  _S_not_equal_to(_SimdWrapper<_Tp, _Np> __x, _SimdWrapper<_Tp, _Np> __y)
 		  __assert_unreachable<_Tp>();
 	      }
 	    else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 8)
-	      return ~_mm512_mask_cmpeq_epi64_mask(__k1, __xi, __yi);
+	      return _mm512_mask_cmpneq_epi64_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 4)
-	      return ~_mm512_mask_cmpeq_epi32_mask(__k1, __xi, __yi);
+	      return _mm512_mask_cmpneq_epi32_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 2)
-	      return ~_mm512_mask_cmpeq_epi16_mask(__k1, __xi, __yi);
+	      return _mm512_mask_cmpneq_epi16_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 64 && sizeof(_Tp) == 1)
-	      return ~_mm512_mask_cmpeq_epi8_mask(__k1, __xi, __yi);
+	      return _mm512_mask_cmpneq_epi8_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 8)
-	      return ~_mm256_mask_cmpeq_epi64_mask(__k1, __xi, __yi);
+	      return _mm256_mask_cmpneq_epi64_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 4)
-	      return ~_mm256_mask_cmpeq_epi32_mask(__k1, __xi, __yi);
+	      return _mm256_mask_cmpneq_epi32_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 2)
-	      return ~_mm256_mask_cmpeq_epi16_mask(__k1, __xi, __yi);
+	      return _mm256_mask_cmpneq_epi16_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 32 && sizeof(_Tp) == 1)
-	      return ~_mm256_mask_cmpeq_epi8_mask(__k1, __xi, __yi);
+	      return _mm256_mask_cmpneq_epi8_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 8)
-	      return ~_mm_mask_cmpeq_epi64_mask(__k1, __xi, __yi);
+	      return _mm_mask_cmpneq_epi64_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 4)
-	      return ~_mm_mask_cmpeq_epi32_mask(__k1, __xi, __yi);
+	      return _mm_mask_cmpneq_epi32_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 2)
-	      return ~_mm_mask_cmpeq_epi16_mask(__k1, __xi, __yi);
+	      return _mm_mask_cmpneq_epi16_mask(__k1, __xi, __yi);
 	    else if constexpr (sizeof(__xi) == 16 && sizeof(_Tp) == 1)
-	      return ~_mm_mask_cmpeq_epi8_mask(__k1, __xi, __yi);
+	      return _mm_mask_cmpneq_epi8_mask(__k1, __xi, __yi);
 	    else
 	      __assert_unreachable<_Tp>();
 	  }                                                   // }}}
@@ -5292,7 +5292,7 @@  _S_find_first_set(simd_mask<_Tp, _Abi> __k)
       _S_find_last_set(simd_mask<_Tp, _Abi> __k)
       {
 	if constexpr (__is_avx512_abi<_Abi>())
-	  return std::__bit_width(__k._M_data._M_data) - 1;
+	  return std::__bit_width(_Abi::_S_masked(__k._M_data)._M_data) - 1;
 	else
 	  return _Base::_S_find_last_set(__k);
       }
diff --git a/libstdc++-v3/testsuite/experimental/simd/pr115454_find_last_set.cc b/libstdc++-v3/testsuite/experimental/simd/pr115454_find_last_set.cc
new file mode 100644
index 00000000000..b47f19d3067
--- /dev/null
+++ b/libstdc++-v3/testsuite/experimental/simd/pr115454_find_last_set.cc
@@ -0,0 +1,49 @@ 
+// { dg-options "-std=gnu++17" }
+// { dg-do run { target *-*-* } }
+// { dg-require-effective-target c++17 }
+// { dg-additional-options "-march=x86-64-v4" { target avx512f } }
+// { dg-require-cmath "" }
+
+#include <experimental/simd>
+
+namespace stdx = std::experimental;
+
+using T = std::uint64_t;
+
+template <typename U, int N>
+using V = stdx::simd<U, stdx::simd_abi::deduce_t<U, N>>;
+
+[[gnu::noinline, gnu::noipa]]
+int reduce(V<T, 4> x)
+{
+  static_assert(stdx::find_last_set(V<T, 4>([](unsigned i) { return i; }) != V<T, 4>(0)) == 3);
+  return stdx::find_last_set(x != -1);
+}
+
+[[gnu::noinline, gnu::noipa]]
+int reduce2()
+{
+  using M8 = typename V<short, 8>::mask_type;
+  using M4 = typename V<int, 4>::mask_type;
+  if constexpr (sizeof(M8) == sizeof(M4))
+    {
+      M4 k;
+      __builtin_memcpy(&__data(k), &__data(M8(true)), sizeof(M4));
+      return stdx::find_last_set(k);
+    }
+  return 3;
+}
+
+
+int main()
+{
+  const V<T, 4> x {};
+
+  const int r = reduce(x);
+  if (r != 3)
+      __builtin_abort();
+
+  const int r2 = reduce2();
+  if (r2 != 3)
+      __builtin_abort();
+}