[PATCH] libstdc++: fix _MaskImplNeon::_S_popcount NEON temporaries on 32-bit Arm [PR122981]
Torbjorn SVENSSON
torbjorn.svensson@foss.st.com
Mon Jul 13 15:10:38 GMT 2026
Hi Dominic,
When you send patches for libstdc++, please don't forget to CC that mailing list (I've added it).
Also, you might want to CC one (or more) of the libstdc++ maintainers and probably also some Arm folks to get a review of your patch.
I'm in no position to say if your patch is acceptable or not.
One note though, the commit message feels very AI-like.
I'm not saying that it's wrong, but it could give a potential reviewer a bad after taste...
Kind regards,
Torbjörn
On 2026-07-13 07:30, Dominic P wrote:
> _MaskImplNeon::_S_popcount initialises NEON intrinsic-typed
> temporaries (int8x8_t, int16x4_t, int32x2_t) directly from generic
> __vector values. On 32-bit Arm the intrinsic vector types are
> distinct from the generic __vector(N) T types, so the C++ front end
> rejects the conversion and the popcount path fails to compile, e.g.:
>
> simd_neon.h:483: error: cannot convert '__vector(8) signed char'
> to 'int8x8_t'
>
> This is the same class of error fixed for _S_to_bits by wrapping the
> vpadd_* operands in __to_intrin, and complements that fix: __to_intrin
> is a same-size reinterpret, so this is a pure type correction with no
> change to the computed result. AArch64 is unaffected because there
> the two vector type families coincide.
>
> libstdc++-v3/ChangeLog:
>
> PR libstdc++/122981
> * include/experimental/bits/simd_neon.h (_MaskImplNeon::
> _S_popcount): Convert the pairwise-add temporaries with
> __to_intrin.
> * testsuite/experimental/simd/pr122981_popcount_neon.cc:
> New test.
>
> Signed-off-by: Dominic P <gcc@gcc.dp11.uk>
> ---
> Hi Torbjörn,
>
> We hit the same failures independently on arm-none-eabi and had an
> equivalent fix queued, so: your patch matches what we tested for the
> _S_to_bits paths, and with it applied pr115454_find_last_set.cc
> compiles and passes here too.
>
> There is one more instance of the same class of error that it does not
> cover: _MaskImplNeon::_S_popcount (around line 481) initialises NEON
> intrinsic-typed temporaries directly from generic __vector values,
>
> int8x8_t __tmp = __lo64(__s8) + __hi64z(__s8);
>
> which the front end rejects on 32-bit Arm for the same reason, so the
> popcount path still fails to compile after your patch. Below is a
> complementary patch (it applies independently of yours - the hunks are
> disjoint) wrapping those three temporaries in __to_intrin, plus a
> popcount test in the style of your find_last_set one.
>
> Tested on arm-none-eabi (armv7-a+neon): the new popcount test fails to
> compile without the patch and compiles with it. AArch64 is unaffected
> by construction - there the intrinsic and generic vector types
> coincide, so __to_intrin is the identity there, as with your patch.
>
> Dominic
>
> .../include/experimental/bits/simd_neon.h | 6 ++--
> .../simd/pr122981_popcount_neon.cc | 31 +++++++++++++++++++
> 2 files changed, 34 insertions(+), 3 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/experimental/simd/pr122981_popcount_neon.cc
>
> diff --git a/libstdc++-v3/include/experimental/bits/simd_neon.h b/libstdc++-v3/include/experimental/bits/simd_neon.h
> index 8fabee7f3..f8cb1a66a 100644
> --- a/libstdc++-v3/include/experimental/bits/simd_neon.h
> +++ b/libstdc++-v3/include/experimental/bits/simd_neon.h
> @@ -481,20 +481,20 @@ template <typename _Abi, typename>
> if constexpr (sizeof(_Tp) == 1)
> {
> const auto __s8 = __vector_bitcast<_SChar>(__k._M_data);
> - int8x8_t __tmp = __lo64(__s8) + __hi64z(__s8);
> + int8x8_t __tmp = __to_intrin(__lo64(__s8) + __hi64z(__s8));
> return -vpadd_s8(vpadd_s8(vpadd_s8(__tmp, int8x8_t()), int8x8_t()),
> int8x8_t())[0];
> }
> else if constexpr (sizeof(_Tp) == 2)
> {
> const auto __s16 = __vector_bitcast<short>(__k._M_data);
> - int16x4_t __tmp = __lo64(__s16) + __hi64z(__s16);
> + int16x4_t __tmp = __to_intrin(__lo64(__s16) + __hi64z(__s16));
> return -vpadd_s16(vpadd_s16(__tmp, int16x4_t()), int16x4_t())[0];
> }
> else if constexpr (sizeof(_Tp) == 4)
> {
> const auto __s32 = __vector_bitcast<int>(__k._M_data);
> - int32x2_t __tmp = __lo64(__s32) + __hi64z(__s32);
> + int32x2_t __tmp = __to_intrin(__lo64(__s32) + __hi64z(__s32));
> return -vpadd_s32(__tmp, int32x2_t())[0];
> }
> else if constexpr (sizeof(_Tp) == 8)
> diff --git a/libstdc++-v3/testsuite/experimental/simd/pr122981_popcount_neon.cc b/libstdc++-v3/testsuite/experimental/simd/pr122981_popcount_neon.cc
> new file mode 100644
> index 000000000..05884e27e
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/experimental/simd/pr122981_popcount_neon.cc
> @@ -0,0 +1,31 @@
> +// { dg-options "-std=gnu++17" }
> +// { dg-do compile { target { arm*-*-* } } }
> +// { dg-require-effective-target arm_neon_ok }
> +// { dg-add-options arm_neon }
> +// PR libstdc++/122981: _MaskImplNeon::_S_popcount initialised NEON
> +// intrinsic-typed temporaries (int8x8_t etc.) from generic __vector
> +// values, which the C++ front end rejects on 32-bit Arm where the
> +// intrinsic and generic vector types are distinct.
> +
> +#include <experimental/simd>
> +
> +namespace stdx = std::experimental;
> +
> +template <typename T>
> + int
> + test_popcount ()
> + {
> + using M = typename stdx::native_simd<T>::mask_type;
> + M k(true);
> + return stdx::popcount (k);
> + }
> +
> +int
> +main ()
> +{
> + int n = 0;
> + n += test_popcount<signed char> ();
> + n += test_popcount<short> ();
> + n += test_popcount<int> ();
> + return n == 0;
> +}
More information about the Libstdc++
mailing list