> -----Original Message-----
> From: [email protected] <[email protected]>
> Sent: 01 July 2026 08:09
> To: [email protected]
> Cc: Kyrylo Tkachov <[email protected]>
> Subject: [PATCH] aarch64: Fold merging svextb/svexth/svextw with a ptrue to
> AND [PR120027]
> 
> From: Kyrylo Tkachov <[email protected]>
> 
> For unsigned types the svextb, svexth and svextw intrinsics are plain
> zero-extends, which the expander already lowers to a bitwise AND with a
> constant mask.  The any/don't-care (_x) form and the zeroing (_z) form with
> an all-true predicate therefore compile to a single unpredicated AND, but
> the merging (_m) form with an all-true predicate did not: it kept the
> inactive argument and produced a predicated UXT.  For example
> 
>   svuint64_t f (svuint64_t x, svuint64_t y)
>   { return svextb_m (y, svptrue_b64 (), x); }
> 
> compiled to
> 
>       ptrue   p3.b, all
>       mov     z31.d, z0.d
>       movprfx z0, z1
>       uxtb    z0.d, p3/m, z31.d
>       ret
> 
> where a single
> 
>       and     z0.d, z0.d, #0xff
>       ret
> 
> is sufficient, because the all-true predicate makes the inactive operand
> dead.
> 
> Give svext_bhw_impl a gimple fold that rewrites the unsigned merging form
> with an all-true predicate to a BIT_AND_EXPR.  Signed types (which use a
> real sign-extend instruction), partial predicates and pfalse predicates are
> left to the existing handling, as is the _x form, which the expander already
> turns into an AND.
> 
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Will push to trunk in a couple of days if now objections.
> 

I think you've been forgetting to CC people on these. So they're just going into
The void.

Tamr

>       PR target/120027
> 
> gcc/ChangeLog:
> 
>       * config/aarch64/aarch64-sve-builtins-base.cc
> (svext_bhw_impl::fold):
>       New member function.  Fold the unsigned svextb/svexth/svextw
>       intrinsics to a bitwise AND when the merging form has an all-true
>       predicate.
> 
> gcc/testsuite/ChangeLog:
> 
>       * gcc.target/aarch64/sve/acle/general/pr120027.c: New test.
> 
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  .../aarch64/aarch64-sve-builtins-base.cc      |  19 ++
>  .../aarch64/sve/acle/general/pr120027.c       | 180 ++++++++++++++++++
>  2 files changed, 199 insertions(+)
>  create mode 100644
> gcc/testsuite/gcc.target/aarch64/sve/acle/general/pr120027.c
> 
> diff --git a/gcc/config/aarch64/aarch64-sve-builtins-base.cc
> b/gcc/config/aarch64/aarch64-sve-builtins-base.cc
> index 963f0adfda1..bfd9641861c 100644
> --- a/gcc/config/aarch64/aarch64-sve-builtins-base.cc
> +++ b/gcc/config/aarch64/aarch64-sve-builtins-base.cc
> @@ -1311,6 +1311,25 @@ public:
>    CONSTEXPR svext_bhw_impl (scalar_int_mode from_mode)
>      : m_from_mode (from_mode) {}
> 
> +  gimple *
> +  fold (gimple_folder &f) const override
> +  {
> +    /* For unsigned types this is a zero-extend, i.e. a bitwise AND with a
> +       constant mask.  The _x form and an all-true _z already lower to that
> +       AND; fold the merging form with an all-true predicate to it too, since
> +       the predicate makes the inactive operand dead.  */
> +    if (f.pred != PRED_m
> +     || !f.type_suffix (0).unsigned_p
> +     || !is_ptrue (f.gp_value (f.call), f.type_suffix (0).element_bytes))
> +      return NULL;
> +
> +    tree op = gimple_call_arg (f.call, 2);
> +    tree mask = build_int_cstu (TREE_TYPE (TREE_TYPE (f.lhs)),
> +                             GET_MODE_MASK (m_from_mode));
> +    tree mask_vec = build_vector_from_val (TREE_TYPE (f.lhs), mask);
> +    return gimple_build_assign (f.lhs, BIT_AND_EXPR, op, mask_vec);
> +  }
> +
>    rtx
>    expand (function_expander &e) const override
>    {
> diff --git a/gcc/testsuite/gcc.target/aarch64/sve/acle/general/pr120027.c
> b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/pr120027.c
> new file mode 100644
> index 00000000000..ba688e1473b
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/sve/acle/general/pr120027.c
> @@ -0,0 +1,180 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +/* Check that the merging form of the unsigned svextb/svexth/svextw
> intrinsics
> +   folds to an unpredicated AND when the governing predicate is all-true, and
> +   that the signed forms keep their sign-extend.  */
> +
> +#include <arm_sve.h>
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/*
> +** uxtb_m_u16_tied:
> +**   and     z0\.h, z0\.h, #0xff
> +**   ret
> +*/
> +svuint16_t
> +uxtb_m_u16_tied (svuint16_t x)
> +{
> +  return svextb_m (x, svptrue_b16 (), x);
> +}
> +
> +/*
> +** uxtb_m_u16_untied:
> +**   movprfx z0, z1
> +**   and     z0\.h, z0\.h, #0xff
> +**   ret
> +*/
> +svuint16_t
> +uxtb_m_u16_untied (svuint16_t inactive, svuint16_t x)
> +{
> +  return svextb_m (inactive, svptrue_b16 (), x);
> +}
> +
> +/*
> +** uxtb_m_u32_tied:
> +**   and     z0\.s, z0\.s, #0xff
> +**   ret
> +*/
> +svuint32_t
> +uxtb_m_u32_tied (svuint32_t x)
> +{
> +  return svextb_m (x, svptrue_b32 (), x);
> +}
> +
> +/*
> +** uxtb_m_u32_untied:
> +**   movprfx z0, z1
> +**   and     z0\.s, z0\.s, #0xff
> +**   ret
> +*/
> +svuint32_t
> +uxtb_m_u32_untied (svuint32_t inactive, svuint32_t x)
> +{
> +  return svextb_m (inactive, svptrue_b32 (), x);
> +}
> +
> +/*
> +** uxtb_m_u64_tied:
> +**   and     z0\.d, z0\.d, #0xff
> +**   ret
> +*/
> +svuint64_t
> +uxtb_m_u64_tied (svuint64_t x)
> +{
> +  return svextb_m (x, svptrue_b64 (), x);
> +}
> +
> +/*
> +** uxtb_m_u64_untied:
> +**   movprfx z0, z1
> +**   and     z0\.d, z0\.d, #0xff
> +**   ret
> +*/
> +svuint64_t
> +uxtb_m_u64_untied (svuint64_t inactive, svuint64_t x)
> +{
> +  return svextb_m (inactive, svptrue_b64 (), x);
> +}
> +
> +/*
> +** uxth_m_u32_tied:
> +**   and     z0\.s, z0\.s, #0xffff
> +**   ret
> +*/
> +svuint32_t
> +uxth_m_u32_tied (svuint32_t x)
> +{
> +  return svexth_m (x, svptrue_b32 (), x);
> +}
> +
> +/*
> +** uxth_m_u64_untied:
> +**   movprfx z0, z1
> +**   and     z0\.d, z0\.d, #0xffff
> +**   ret
> +*/
> +svuint64_t
> +uxth_m_u64_untied (svuint64_t inactive, svuint64_t x)
> +{
> +  return svexth_m (inactive, svptrue_b64 (), x);
> +}
> +
> +/*
> +** uxtw_m_u64_tied:
> +**   and     z0\.d, z0\.d, #0xffffffff
> +**   ret
> +*/
> +svuint64_t
> +uxtw_m_u64_tied (svuint64_t x)
> +{
> +  return svextw_m (x, svptrue_b64 (), x);
> +}
> +
> +/*
> +** uxtw_m_u64_untied:
> +**   movprfx z0, z1
> +**   and     z0\.d, z0\.d, #0xffffffff
> +**   ret
> +*/
> +svuint64_t
> +uxtw_m_u64_untied (svuint64_t inactive, svuint64_t x)
> +{
> +  return svextw_m (inactive, svptrue_b64 (), x);
> +}
> +
> +/*
> +** sxtb_m_s32_tied:
> +**   ptrue   (p[0-7])\.b, all
> +**   sxtb    z0\.s, \1/m, z0\.s
> +**   ret
> +*/
> +svint32_t
> +sxtb_m_s32_tied (svint32_t x)
> +{
> +  return svextb_m (x, svptrue_b32 (), x);
> +}
> +
> +/*
> +** sxtb_m_s32_untied:
> +**   ptrue   (p[0-7])\.b, all
> +**   sxtb    z0\.s, \1/m, z1\.s
> +**   ret
> +*/
> +svint32_t
> +sxtb_m_s32_untied (svint32_t inactive, svint32_t x)
> +{
> +  return svextb_m (inactive, svptrue_b32 (), x);
> +}
> +
> +/*
> +** sxth_m_s64_tied:
> +**   ptrue   (p[0-7])\.b, all
> +**   sxth    z0\.d, \1/m, z0\.d
> +**   ret
> +*/
> +svint64_t
> +sxth_m_s64_tied (svint64_t x)
> +{
> +  return svexth_m (x, svptrue_b64 (), x);
> +}
> +
> +/*
> +** sxtw_m_s64_tied:
> +**   ptrue   (p[0-7])\.b, all
> +**   sxtw    z0\.d, \1/m, z0\.d
> +**   ret
> +*/
> +svint64_t
> +sxtw_m_s64_tied (svint64_t x)
> +{
> +  return svextw_m (x, svptrue_b64 (), x);
> +}
> +
> +#ifdef __cplusplus
> +}
> +#endif
> --
> 2.50.1 (Apple Git-155)

Reply via email to