Re: [PATCH] match.pd: Handle shift-and-subtract SWAR sign masks

huzife <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <CAP72AsGzrd0xNDYUPSBZz=bYp5QLW-kuiUXxGfXsTcYzMGWZ5Q@mail.gmail.com>
Hi, ping for this patch:

https://gcc.gnu.org/pipermail/gcc-patches/2026-August/726330.html

Thanks,
huzife


On Tue, Aug 4, 2026 at 8:45 PM huzife <[email protected]> wrote:
>
> The existing SWAR sign-mask simplification handles a shifted and masked
> value multiplied by an all-ones value.  The equivalent (x << N) - x form
> is not recognized.
>
> Canonicalize the shift-and-subtract form to multiplication so that it can
> reuse the existing simplification.  Require unsigned vectors, exact shift
> and mask constants, valid vector geometry, and target support for the
> resulting comparison and conditional operation.
>
> gcc/ChangeLog:
>
>         * match.pd: Canonicalize shift-and-subtract SWAR sign masks to
>         multiplication.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.target/aarch64/swar_to_vec_cmp_2.c: New test.
>         * gcc.target/aarch64/swar_to_vec_cmp_3.c: New test.
>
> Signed-off-by: huzife <[email protected]>
> ---
>  gcc/match.pd                                  | 60 +++++++++++++++++
>  .../gcc.target/aarch64/swar_to_vec_cmp_2.c    | 65 +++++++++++++++++++
>  .../gcc.target/aarch64/swar_to_vec_cmp_3.c    | 60 +++++++++++++++++
>  3 files changed, 185 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/swar_to_vec_cmp_2.c
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/swar_to_vec_cmp_3.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 293650760e2..087f7636867 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -408,6 +408,66 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>      (view_convert (bit_and:itype (view_convert @0)
>                                  (ne @1 { build_zero_cst (type); })))))))
>
> +/* Canonicalize the shift-and-subtract form of a SWAR sign mask to the
> +   equivalent multiplication form below.  Check the same shape and target
> +   requirements here, so that this cannot introduce an unsupported vector
> +   multiply.  */
> +#if GIMPLE
> +(simplify
> + (minus
> +  (lshift (bit_and@4
> +            (rshift @0 uniform_integer_cst_p@1)
> +            uniform_integer_cst_p@2)
> +          uniform_integer_cst_p@3)
> +  @4)
> + (with {
> +   tree rshift_cst = uniform_integer_cst_p (@1);
> +   tree bit_and_cst = uniform_integer_cst_p (@2);
> +   tree lshift_cst = uniform_integer_cst_p (@3);
> +  }
> +  (if (VECTOR_TYPE_P (type)
> +       && TYPE_UNSIGNED (TREE_TYPE (type))
> +       && tree_fits_uhwi_p (rshift_cst)
> +       && tree_fits_uhwi_p (bit_and_cst)
> +       && tree_fits_uhwi_p (lshift_cst)
> +       && tree_to_uhwi (rshift_cst) < HOST_BITS_PER_WIDE_INT - 1)
> +   (with {
> +     HOST_WIDE_INT vec_elem_bits = vector_element_bits (type);
> +     poly_int64 vec_nelts = TYPE_VECTOR_SUBPARTS (type);
> +     poly_int64 vec_bits = vec_elem_bits * vec_nelts;
> +     unsigned HOST_WIDE_INT cmp_bits_i, bit_and_i, target_bit_and_i;
> +     cmp_bits_i = tree_to_uhwi (rshift_cst) + 1;
> +     bit_and_i = tree_to_uhwi (bit_and_cst);
> +     target_bit_and_i = 0;
> +
> +     for (unsigned i = 0; i < vec_elem_bits / cmp_bits_i; i++)
> +       target_bit_and_i = (target_bit_and_i << cmp_bits_i) | 1U;
> +    }
> +    (if (exact_log2 (cmp_bits_i) >= 0
> +        && cmp_bits_i < HOST_BITS_PER_WIDE_INT
> +        && cmp_bits_i < (unsigned HOST_WIDE_INT) vec_elem_bits
> +        && tree_to_uhwi (lshift_cst) == cmp_bits_i
> +        && multiple_p (vec_bits, cmp_bits_i)
> +        && vec_elem_bits <= HOST_BITS_PER_WIDE_INT
> +        && target_bit_and_i == bit_and_i)
> +     (with {
> +       tree cmp_type = build_nonstandard_integer_type (cmp_bits_i, 0);
> +       poly_int64 vector_type_nelts = exact_div (vec_bits, cmp_bits_i);
> +       tree vec_cmp_type = build_vector_type (cmp_type, vector_type_nelts);
> +       tree vec_truth_type = truth_type_for (vec_cmp_type);
> +      }
> +      (if (expand_vec_cmp_expr_p (vec_cmp_type, vec_truth_type, LT_EXPR)
> +          && expand_vec_cond_expr_p (vec_cmp_type, vec_truth_type))
> +       (with {
> +        wide_int multiplier = wi::mask (cmp_bits_i, false,
> +                                       element_precision (type));
> +        tree multiplier_cst
> +          = wide_int_to_tree (TREE_TYPE (type), multiplier);
> +        multiplier_cst = build_uniform_cst (type, multiplier_cst);
> +       }
> +       (mult @4 { multiplier_cst; })))))))))
> +#endif
> +
>  /* In SWAR (SIMD within a register) code a signed comparison of packed data
>     can be constructed with a particular combination of shift, bitwise and,
>     and multiplication by constants.  If that code is vectorized we can
> diff --git a/gcc/testsuite/gcc.target/aarch64/swar_to_vec_cmp_2.c b/gcc/testsuite/gcc.target/aarch64/swar_to_vec_cmp_2.c
> new file mode 100644
> index 00000000000..4722d3c1661
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/swar_to_vec_cmp_2.c
> @@ -0,0 +1,65 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +
> +typedef unsigned short v4hi __attribute__ ((vector_size (8)));
> +typedef unsigned short v8hi __attribute__ ((vector_size (16)));
> +typedef unsigned int v2si __attribute__ ((vector_size (8)));
> +typedef unsigned int v4si __attribute__ ((vector_size (16)));
> +typedef unsigned long long v2di __attribute__ ((vector_size (16)));
> +
> +v4hi
> +packed_cmp_v4hi (v4hi a)
> +{
> +  const v4hi right = { 7, 7, 7, 7 };
> +  const v4hi left = { 8, 8, 8, 8 };
> +  const v4hi mask = { 0x101, 0x101, 0x101, 0x101 };
> +  v4hi x = (a >> right) & mask;
> +  return (x << left) - x;
> +}
> +
> +v8hi
> +packed_cmp_v8hi (v8hi a)
> +{
> +  const v8hi right = { 7, 7, 7, 7, 7, 7, 7, 7 };
> +  const v8hi left = { 8, 8, 8, 8, 8, 8, 8, 8 };
> +  const v8hi mask = { 0x101, 0x101, 0x101, 0x101,
> +                     0x101, 0x101, 0x101, 0x101 };
> +  v8hi x = (a >> right) & mask;
> +  return (x << left) - x;
> +}
> +
> +v2si
> +packed_cmp_v2si (v2si a)
> +{
> +  const v2si right = { 15, 15 };
> +  const v2si left = { 16, 16 };
> +  const v2si mask = { 0x10001, 0x10001 };
> +  v2si x = (a >> right) & mask;
> +  return (x << left) - x;
> +}
> +
> +v4si
> +packed_cmp_v4si (v4si a)
> +{
> +  const v4si right = { 15, 15, 15, 15 };
> +  const v4si left = { 16, 16, 16, 16 };
> +  const v4si mask = { 0x10001, 0x10001, 0x10001, 0x10001 };
> +  v4si x = (a >> right) & mask;
> +  return (x << left) - x;
> +}
> +
> +v2di
> +packed_cmp_v2di (v2di a)
> +{
> +  const v2di right = { 31, 31 };
> +  const v2di left = { 32, 32 };
> +  const v2di mask = { 0x100000001ULL, 0x100000001ULL };
> +  v2di x = (a >> right) & mask;
> +  return (x << left) - x;
> +}
> +
> +/* { dg-final { scan-assembler-times {\tcmlt\tv[0-9]+\.8b,} 1 } } */
> +/* { dg-final { scan-assembler-times {\tcmlt\tv[0-9]+\.16b,} 1 } } */
> +/* { dg-final { scan-assembler-times {\tcmlt\tv[0-9]+\.4h,} 1 } } */
> +/* { dg-final { scan-assembler-times {\tcmlt\tv[0-9]+\.8h,} 1 } } */
> +/* { dg-final { scan-assembler-times {\tcmlt\tv[0-9]+\.4s,} 1 } } */
> diff --git a/gcc/testsuite/gcc.target/aarch64/swar_to_vec_cmp_3.c b/gcc/testsuite/gcc.target/aarch64/swar_to_vec_cmp_3.c
> new file mode 100644
> index 00000000000..861ed3e58c5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/swar_to_vec_cmp_3.c
> @@ -0,0 +1,60 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +
> +typedef unsigned long long v2di __attribute__ ((vector_size (16)));
> +typedef long long signed_v2di __attribute__ ((vector_size (16)));
> +
> +v2di
> +wrong_mask (v2di a)
> +{
> +  const v2di right = { 31, 31 };
> +  const v2di left = { 32, 32 };
> +  const v2di mask = { 0x100000000ULL, 0x100000000ULL };
> +  v2di x = (a >> right) & mask;
> +  return (x << left) - x;
> +}
> +
> +v2di
> +wrong_right_shift (v2di a)
> +{
> +  const v2di right = { 30, 30 };
> +  const v2di left = { 32, 32 };
> +  const v2di mask = { 0x100000001ULL, 0x100000001ULL };
> +  v2di x = (a >> right) & mask;
> +  return (x << left) - x;
> +}
> +
> +v2di
> +wrong_left_shift (v2di a)
> +{
> +  const v2di right = { 31, 31 };
> +  const v2di left = { 31, 31 };
> +  const v2di mask = { 0x100000001ULL, 0x100000001ULL };
> +  v2di x = (a >> right) & mask;
> +  return (x << left) - x;
> +}
> +
> +v2di
> +different_operands (v2di a, v2di b)
> +{
> +  const v2di right = { 31, 31 };
> +  const v2di left = { 32, 32 };
> +  const v2di mask = { 0x100000001ULL, 0x100000001ULL };
> +  v2di x = (a >> right) & mask;
> +  v2di y = (b >> right) & mask;
> +  return (x << left) - y;
> +}
> +
> +signed_v2di
> +signed_type (signed_v2di a)
> +{
> +  const signed_v2di right = { 31, 31 };
> +  const signed_v2di left = { 32, 32 };
> +  const signed_v2di input_mask = { 0x7fffffffffffffffLL,
> +                                   0x7fffffffffffffffLL };
> +  const signed_v2di mask = { 0x100000001LL, 0x100000001LL };
> +  signed_v2di x = ((a & input_mask) >> right) & mask;
> +  return (x << left) - x;
> +}
> +
> +/* { dg-final { scan-assembler-not {\tcmlt\t} } } */
> --
> 2.43.0
>
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.