[PATCH] match.pd: Handle shift-and-subtract SWAR sign masks
huzife <[email protected]> Tue, 4 Aug 2026 20:46:47 +0800
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <[email protected]> |
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