Re: [PATCH] libstdc++: arm: convert vpadd_* operands to intrinsic types [PR122981]

Torbjorn SVENSSON <[email protected]>
Newsgroups gmane.comp.gcc.patches,gmane.comp.gcc.libstdc++.devel
Message-ID <[email protected]>
Hi Matthias,

Thanks for the reply.

On 2026-08-13 16:22, Matthias Kretz wrote:
> Thanks Torbjorn,
> 
> and sorry for not looking earlier (yes, vacation and the madness that comes
> after).

No worries! :)

> I'm fairly certain that the code worked at some point. Actually, a quick test
> on Compiler Explorer and I can't reproduce the issue. But I must admit that
> I'm lost to all the variants of ARM, and I'm probably not testing the one that
> fails for you.
> 
> Can you show me how to compile https://compiler-explorer.com/z/jWPEPTo8a so
> that it requires the cast to intrinsic type?

First of all, the toolchain that you select is a linux based and it has different
rules than arm-none-eabi.
Regardless, the problem happens when the GNU vector has different size than what
NEON can work with.

In your example at https://compiler-explorer.com/z/jWPEPTo8a, simply change to

using V [[gnu::vector_size(4)]] = signed char;

and you will see the problem, regardless if you are using linux ABI or none ABI.

Calling the std::experimental::parallelism_v2::__to_intrin() before would ensure
that the input value matches the expected vector size.

Here is an updated example that has both a working variant and a failing one:
https://compiler-explorer.com/z/aWz1P4Kdj

> Also, if you know of a better way to turn a vector-mask into a bit-mask, I'd
> be happy to replace that code altogether.

Sorry, don't think I'm the right person for this task. :|

Kind regards,
Torbjörn

> 
> - Matthias
> 
> Torbjorn SVENSSON [Thursday, 6 August 2026, 17:55:25 CEST]:
>> On 2026-07-24 18:34, Jonathan Wakely wrote:
>>> On Sun, 12 Jul 2026 at 19:42 +0200, Torbjörn SVENSSON wrote:
>>>> I hope this is the right fix for the issue.
>>>> I've regtested for arm-none-eabi on top of r15-11320-g7909a9f85f30b6 and
>>>> it applies cleanly on trunk. The new test is derived from
>>>> libstdc++-v3/testsuite/experimental/simd/pr115454_find_last_set.cc.
>>>>
>>>> Ok for trunk, releases/gcc-16 and releases/gcc-15?
>>>
>>> OK for trunk but for the backports I'd prefer to wait for Matthias to
>>> confirm this is the correct fix.
>>
>> I've been holding of to push this since I do not want to push something that
>> might not be correct. Do you have any idea when Matthias is back (assuming
>> vacation period right now)?
>>
>> Kind regards,
>> Torbjörn
>>
>>>> When testing r15-10798-gae573c9d0e7f1c, I noticed that the
>>>> experimental/simd/pr115454_find_last_set.cc was failing due to that the
>>>> GNU vector type was used instead of the NEON intrinsic type.
>>>>
>>>>      PR libstdc++/122981
>>>>
>>>> libstdc++-v3/ChangeLog:
>>>>
>>>>      * include/experimental/bits/simd_neon.h: Convert AArch32 NEON
>>>>      vpadd_* operands to intrinsic types.
>>>>      * testsuite/experimental/simd/pr122981_find_last_set_neon.cc: New
>>>> test.
>>>>
>>>> Signed-off-by: Torbjörn SVENSSON <[email protected]>
>>>> ---
>>>> .../include/experimental/bits/simd_neon.h     | 31 +++++++-----
>>>> .../simd/pr122981_find_last_set_neon.cc       | 50 +++++++++++++++++++
>>>> 2 files changed, 69 insertions(+), 12 deletions(-)
>>>> create mode 100644
>>>> libstdc++-v3/testsuite/experimental/simd/pr122981_find_last_set_neon.cc
>>>>
>>>> diff --git a/libstdc++-v3/include/experimental/bits/simd_neon.h
>>>> b/libstdc++-v3/include/experimental/bits/simd_neon.h index
>>>> d3785db2bfd..23c205f3dc9 100644
>>>> --- a/libstdc++-v3/include/experimental/bits/simd_neon.h
>>>> +++ b/libstdc++-v3/include/experimental/bits/simd_neon.h
>>>> @@ -297,9 +297,10 @@ struct _MaskImplNeonMixin
>>>>                __zero))[0];
>>>> #else
>>>>            return __vector_bitcast<_UShort>(
>>>> -        vpadd_s8(vpadd_s8(vpadd_s8(__lo64(__asint), __hi64(__asint)),
>>>> -                  __zero),
>>>> -             __zero))[0];
>>>> +        vpadd_s8(vpadd_s8(vpadd_s8(__to_intrin(__lo64(__asint)),
>>>> +                        __to_intrin(__hi64(__asint))),
>>>> +                  __to_intrin(__zero)),
>>>> +             __to_intrin(__zero)))[0];
>>>> #endif
>>>>          }
>>>>        else if constexpr (sizeof(_Tp) == 2)
>>>> @@ -313,9 +314,10 @@ struct _MaskImplNeonMixin
>>>> #ifdef __aarch64__
>>>>            return vaddvq_s16(__asint);
>>>> #else
>>>> -          return vpadd_s16(
>>>> -        vpadd_s16(vpadd_s16(__lo64(__asint), __hi64(__asint)), __zero),
>>>> -        __zero)[0];
>>>> +          return
>>>> vpadd_s16(vpadd_s16(vpadd_s16(__to_intrin(__lo64(__asint)),
>>>> +                           __to_intrin(__hi64(__asint))),
>>>> +                     __to_intrin(__zero)),
>>>> +                   __to_intrin(__zero))[0];
>>>> #endif
>>>>          }
>>>>        else if constexpr (sizeof(_Tp) == 4)
>>>> @@ -329,8 +331,9 @@ struct _MaskImplNeonMixin
>>>> #ifdef __aarch64__
>>>>            return vaddvq_s32(__asint);
>>>> #else
>>>> -          return vpadd_s32(vpadd_s32(__lo64(__asint), __hi64(__asint)),
>>>> -                   __zero)[0];
>>>> +          return vpadd_s32(vpadd_s32(__to_intrin(__lo64(__asint)),
>>>> +                     __to_intrin(__hi64(__asint))),
>>>> +                   __to_intrin(__zero))[0];
>>>> #endif
>>>>          }
>>>>        else if constexpr (sizeof(_Tp) == 8)
>>>> @@ -353,8 +356,10 @@ struct _MaskImplNeonMixin
>>>> #ifdef __aarch64__
>>>>            return vaddv_s8(__asint);
>>>> #else
>>>> -          return vpadd_s8(vpadd_s8(vpadd_s8(__asint, __zero), __zero),
>>>> -                  __zero)[0];
>>>> +          return vpadd_s8(vpadd_s8(vpadd_s8(__to_intrin(__asint),
>>>> +                        __to_intrin(__zero)),
>>>> +                       __to_intrin(__zero)),
>>>> +                  __to_intrin(__zero))[0];
>>>> #endif
>>>>          }
>>>>        else if constexpr (sizeof(_Tp) == 2)
>>>> @@ -368,7 +373,9 @@ struct _MaskImplNeonMixin
>>>> #ifdef __aarch64__
>>>>            return vaddv_s16(__asint);
>>>> #else
>>>> -          return vpadd_s16(vpadd_s16(__asint, __zero), __zero)[0];
>>>> +          return vpadd_s16(vpadd_s16(__to_intrin(__asint),
>>>> +                     __to_intrin(__zero)),
>>>> +                   __to_intrin(__zero))[0];
>>>> #endif
>>>>          }
>>>>        else if constexpr (sizeof(_Tp) == 4)
>>>> @@ -377,7 +384,7 @@ struct _MaskImplNeonMixin
>>>> #ifdef __aarch64__
>>>>            return vaddv_s32(__asint);
>>>> #else
>>>> -          return vpadd_s32(__asint, __zero)[0];
>>>> +          return vpadd_s32(__to_intrin(__asint),
>>>> __to_intrin(__zero))[0];
>>>> #endif
>>>>          }
>>>>        else
>>>> diff --git
>>>> a/libstdc++-v3/testsuite/experimental/simd/pr122981_find_last_set_neon.c
>>>> c
>>>> b/libstdc++-v3/testsuite/experimental/simd/pr122981_find_last_set_neon.c
>>>> c new file mode 100644
>>>> index 00000000000..f7f79166ae6
>>>> --- /dev/null
>>>> +++
>>>> b/libstdc++-v3/testsuite/experimental/simd/pr122981_find_last_set_neon.c
>>>> c @@ -0,0 +1,50 @@
>>>> +// { dg-options "-std=gnu++17" }
>>>> +// { dg-do compile { target c++17 } }
>>>> +// { dg-require-effective-target arm_neon_ok }
>>>> +// { dg-add-options arm_neon }
>>>> +// { dg-require-cmath "" }
>>>> +
>>>> +#include <experimental/simd>
>>>> +
>>>> +namespace stdx = std::experimental;
>>>> +
>>>> +template <typename U, int N>
>>>> +using V = stdx::simd<U, stdx::simd_abi::deduce_t<U, N>>;
>>>> +
>>>> +template <typename U, int N>
>>>> +[[gnu::noinline, gnu::noipa]]
>>>> +int reduce(typename V<U, N>::mask_type x)
>>>> +{
>>>> +  using M = typename V<U, N>::mask_type;
>>>> +  static_assert(stdx::find_last_set(M(true)) == N - 1);
>>>> +  return stdx::find_last_set(x);
>>>> +}
>>>> +
>>>> +int main()
>>>> +{
>>>> +  // 16 bytes: 16 int8_t lanes, 8 int16_t lanes, 4 int32_t lanes.
>>>> +  const int r_int8_16 = reduce<std::int8_t, 16>(typename V<std::int8_t,
>>>> 16>::mask_type(true)); +  if (r_int8_16 != 15)
>>>> +    __builtin_abort();
>>>> +
>>>> +  const int r_int16_8 = reduce<std::int16_t, 8>(typename V<std::int16_t,
>>>> 8>::mask_type(true)); +  if (r_int16_8 != 7)
>>>> +    __builtin_abort();
>>>> +
>>>> +  const int r_int32_4 = reduce<std::int32_t, 4>(typename V<std::int32_t,
>>>> 4>::mask_type(true)); +  if (r_int32_4 != 3)
>>>> +    __builtin_abort();
>>>> +
>>>> +  // 8 bytes: 8 int8_t lanes, 4 int16_t lanes, 2 int32_t lanes.
>>>> +  const int r_int8_8 = reduce<std::int8_t, 8>(typename V<std::int8_t,
>>>> 8>::mask_type(true)); +  if (r_int8_8 != 7)
>>>> +    __builtin_abort();
>>>> +
>>>> +  const int r_int16_4 = reduce<std::int16_t, 4>(typename V<std::int16_t,
>>>> 4>::mask_type(true)); +  if (r_int16_4 != 3)
>>>> +    __builtin_abort();
>>>> +
>>>> +  const int r_int32_2 = reduce<std::int32_t, 2>(typename V<std::int32_t,
>>>> 2>::mask_type(true)); +  if (r_int32_2 != 1)
>>>> +    __builtin_abort();
>>>> +}
> 
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.