https://gcc.gnu.org/g:f630eb20113fa594480583e65c1eaf7daac64bf0
commit r17-5589-gf630eb20113fa594480583e65c1eaf7daac64bf0 Author: Igor Shevlyakov <[email protected]> Date: Sat Oct 24 13:03:20 2026 -0700 match.pd: Don't fold wrapping signed (x + y) >> 1 to AVG_FLOOR [PR128142] The fold of (x + y) >> 1 into IFN_AVG_FLOOR (x, y) for vectors checks that each element of x and y has a leading zero bit, so that x + y cannot wrap. That is enough for an unsigned type, or for a signed one whose overflow is undefined, but with -fwrapv two nonnegative signed values can add up to a negative one. For example v4si f (v4si a, v4si b) { return ((a & 0x7fffffff) + (b & 0x7fffffff)) >> 1; } with a = b = { INT_MAX, ... } returns -1 per lane, but became .AVG_FLOOR (a & 0x7fffffff, b & 0x7fffffff), which is INT_MAX: an shadd on AArch64. Require two leading zero bits for a signed type whose overflow is defined. Assisted-by: Claude Opus 5.5 PR tree-optimization/128142 gcc/ChangeLog: * match.pd ((x + y) >> 1 -> IFN_AVG_FLOOR (x, y)): Require two leading zero bits for a signed type whose overflow is defined. gcc/testsuite/ChangeLog: * gcc.dg/torture/pr128142.c: New test. * gcc.target/aarch64/pr128142.c: New test. Signed-off-by: Igor Shevlyakov <[email protected]> Diff: --- gcc/match.pd | 19 +++++++++++++----- gcc/testsuite/gcc.dg/torture/pr128142.c | 25 +++++++++++++++++++++++ gcc/testsuite/gcc.target/aarch64/pr128142.c | 31 +++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/gcc/match.pd b/gcc/match.pd index 0bda1efac591..86954e5508bd 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -2605,14 +2605,23 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) @0)) /* Fold ((x + y) >> 1 into IFN_AVG_FLOOR (x, y) if x and y are vectors in - which each element is known to have at least one leading zero bit. */ + which x + y cannot wrap: each element is known to have at least one + leading zero bit, or two if the type is signed and its overflow is + defined, since the sum of two nonnegative values can then wrap to a + negative one. */ (simplify (rshift (plus:cs @0 @1) integer_onep) (if (VECTOR_TYPE_P (type) - && direct_internal_fn_supported_p (IFN_AVG_FLOOR, type, OPTIMIZE_FOR_BOTH) - && wi::clz (get_nonzero_bits (@0)) > 0 - && wi::clz (get_nonzero_bits (@1)) > 0) - (IFN_AVG_FLOOR @0 @1))) + && direct_internal_fn_supported_p (IFN_AVG_FLOOR, type, + OPTIMIZE_FOR_BOTH)) + (with + { + int min_clz = (TYPE_UNSIGNED (type) || TYPE_OVERFLOW_UNDEFINED (type) + ? 1 : 2); + } + (if (wi::clz (get_nonzero_bits (@0)) >= min_clz + && wi::clz (get_nonzero_bits (@1)) >= min_clz) + (IFN_AVG_FLOOR @0 @1))))) #endif /* Try to fold (type) X op CST -> (type) (X op ((type-x) CST)) diff --git a/gcc/testsuite/gcc.dg/torture/pr128142.c b/gcc/testsuite/gcc.dg/torture/pr128142.c new file mode 100644 index 000000000000..f9704b98fc76 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr128142.c @@ -0,0 +1,25 @@ +/* PR tree-optimization/128142 */ +/* With -fwrapv the sum of two nonnegative shorts can wrap to a negative + value, so (x + y) >> 1 must not become an average. */ +/* { dg-do run } */ +/* { dg-additional-options "-fwrapv -Wno-psabi" } */ + +typedef short v8hi __attribute__ ((vector_size (16))); + +__attribute__ ((noipa)) v8hi +f (v8hi a, v8hi b) +{ + return ((a & 0x7fff) + (b & 0x7fff)) >> 1; +} + +int +main () +{ + v8hi a = { 0x7fff, 0x7fff, 0x4000, 3, 0, -1, 0x7ffe, 100 }; + v8hi b = { 0x7fff, 1, 0x4000, 4, 0, -1, 1, 200 }; + v8hi r = f (a, b); + if (r[0] != -1 || r[1] != -0x4000 || r[2] != -0x4000 || r[3] != 3 + || r[4] != 0 || r[5] != -1 || r[6] != 0x3fff || r[7] != 150) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.target/aarch64/pr128142.c b/gcc/testsuite/gcc.target/aarch64/pr128142.c new file mode 100644 index 000000000000..acbbfbb2be2c --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/pr128142.c @@ -0,0 +1,31 @@ +/* PR tree-optimization/128142 */ +/* With -fwrapv, (x + y) >> 1 is an average only if x + y cannot wrap. */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fwrapv" } */ + +typedef int v4si __attribute__ ((vector_size (16))); +typedef unsigned int v4usi __attribute__ ((vector_size (16))); + +/* Signed with one leading zero bit: the sum can wrap. */ +v4si +f1 (v4si a, v4si b) +{ + return ((a & 0x7fffffff) + (b & 0x7fffffff)) >> 1; +} + +/* Signed with two leading zero bits: the sum cannot wrap. */ +v4si +f2 (v4si a, v4si b) +{ + return ((a & 0x3fffffff) + (b & 0x3fffffff)) >> 1; +} + +/* Unsigned with one leading zero bit: the sum cannot wrap. */ +v4usi +f3 (v4usi a, v4usi b) +{ + return ((a & 0x7fffffff) + (b & 0x7fffffff)) >> 1; +} + +/* { dg-final { scan-assembler-times {\tshadd\tv[0-9]+\.4s,} 1 } } */ +/* { dg-final { scan-assembler-times {\tuhadd\tv[0-9]+\.4s,} 1 } } */
