https://gcc.gnu.org/g:6c9deef7effbf410e5d4061c4ea6f53e1dca0bad
commit r17-5558-g6c9deef7effbf410e5d4061c4ea6f53e1dca0bad Author: Alexey Lapshin <[email protected]> Date: Sun Oct 25 08:03:34 2026 -0600 [PATCH v3] match.pd: fold floating compare-and-negate idioms to abs PR tree-optimization/101039 folded A CMP 0 ? A : -A after that form was expanded into an if, by matching it in match.pd (r12-2041). The true arm there is A and the false arm is -A. The swapped form still does not fold for floating point. Integers reach the existing pattern by inverting the comparison. A floating comparison that may trap on NaNs cannot be inverted: the inverse of < is UNGE, and UNGE does not raise invalid. Fold A </<= 0 ? -A : A to ABS (A) and A >/>= 0 ? -A : A to -ABS (A) when signed zeros need not be preserved. ABS does not raise invalid, so the ordered compare is removed only when the operand cannot be a NaN, or when the invalid exception is not observable. A conversion from an integer cannot be a NaN. A NaN selects either arm, and both arms are NaNs. Vectors are folded only when the target can do ABS (and NEGATE for the -ABS form). gcc: * match.pd (A </<= 0 ? -A : A, A >/>= 0 ? -A : A): New patterns. gcc/testsuite/ * gcc.dg/tree-ssa/abs-5.c: New test. * gcc.dg/tree-ssa/abs-6.c: New test. * gcc.dg/tree-ssa/phi-opt-53.c: New test. Diff: --- gcc/match.pd | 24 ++++++++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/abs-5.c | 28 ++++++++++++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/abs-6.c | 13 +++++++++++ gcc/testsuite/gcc.dg/tree-ssa/phi-opt-53.c | 35 ++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+) diff --git a/gcc/match.pd b/gcc/match.pd index 696e69fd980c..0bda1efac591 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -7858,6 +7858,30 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (convert (negate (absu:utype (convert:stype @2)))))) (convert (negate (abs @2)))))) ) + /* A </<= 0 ? -A : A same as abs (A) + A >/>= 0 ? -A : A same as -abs (A) + For integers these are reached by inverting the comparison of the + forms above, but a floating-point comparison that may trap on NaNs + cannot be inverted. ABS does not trap, so the ordered compare may + be removed only when this operand cannot be a NaN or the invalid + exception is not observable. */ + (for cmp (lt le) + (simplify + (cnd (cmp @0 real_zerop) (negate @0) @0) + (if (!HONOR_SIGNED_ZEROS (type) + && !(tree_expr_maybe_nan_p (@0) && flag_trapping_math) + && (!VECTOR_TYPE_P (type) + || target_supports_op_p (type, ABS_EXPR, optab_vector))) + (abs @0)))) + (for cmp (gt ge) + (simplify + (cnd (cmp @0 real_zerop) (negate @0) @0) + (if (!HONOR_SIGNED_ZEROS (type) + && !(tree_expr_maybe_nan_p (@0) && flag_trapping_math) + && (!VECTOR_TYPE_P (type) + || (target_supports_op_p (type, ABS_EXPR, optab_vector) + && target_supports_op_p (type, NEGATE_EXPR, optab_vector)))) + (negate (abs @0))))) /* (A - B) == 0 ? (A - B) : (B - A) same as (B - A) */ (for cmp (eq uneq) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/abs-5.c b/gcc/testsuite/gcc.dg/tree-ssa/abs-5.c new file mode 100644 index 000000000000..dcbf17f40233 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/abs-5.c @@ -0,0 +1,28 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-signed-zeros -fno-trapping-math -fdump-tree-optimized" } */ + +/* Fold, when signed zeros need not be preserved: + A </<= 0 ? -A : A -> ABS (A) + A >/>= 0 ? -A : A -> -ABS (A) + + ABS does not trap. With NaNs honored, -fno-trapping-math is required + so the ordered compare's invalid exception is not observable. The + compare is matched directly: its inverse is UNGE. Both arms are NaN + when A is NaN. */ + +float abs_lt (float a) { return a < 0 ? -a : a; } +float abs_le (float a) { return a <= 0 ? -a : a; } +float abs_if (float a) { if (a < 0) a = -a; return a; } +double abs_lt_d (double a) { return a < 0 ? -a : a; } + +float nabs_gt (float a) { return a > 0 ? -a : a; } +float nabs_ge (float a) { return a >= 0 ? -a : a; } + +/* -ABS becomes .COPYSIGN (x, -1) when the target has the copysign optab. */ +/* { dg-final { scan-tree-dump-times "= ABS_EXPR" 4 "optimized" { target ifn_copysign } } } */ +/* { dg-final { scan-tree-dump-times "= \\.COPYSIGN" 2 "optimized" { target ifn_copysign } } } */ +/* { dg-final { scan-tree-dump-times "= ABS_EXPR" 6 "optimized" { target { ! ifn_copysign } } } } */ +/* { dg-final { scan-tree-dump-not " < " "optimized" } } */ +/* { dg-final { scan-tree-dump-not " <= " "optimized" } } */ +/* { dg-final { scan-tree-dump-not " > " "optimized" } } */ +/* { dg-final { scan-tree-dump-not " >= " "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/abs-6.c b/gcc/testsuite/gcc.dg/tree-ssa/abs-6.c new file mode 100644 index 000000000000..837208137642 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/abs-6.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-signed-zeros -fdump-tree-optimized" } */ + +/* Trapping math stays on. A float parameter can be a NaN, so the + ordered compare must not become ABS. A conversion from an integer + cannot be a NaN, so that compare can. */ + +float keep_cmp (float a) { return a < 0 ? -a : a; } + +float from_int (int i) { return (float)i < 0 ? -(float)i : (float)i; } + +/* { dg-final { scan-tree-dump-times "= ABS_EXPR" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump " < " "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-53.c b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-53.c new file mode 100644 index 000000000000..33a28d675b28 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/phi-opt-53.c @@ -0,0 +1,35 @@ +/* { dg-options "-O2 -fno-signed-zeros -fno-trapping-math -fdump-tree-phiopt" } */ + +/* Swapped arms of phi-opt-24.c. A < 0 ? -A : A is abs (A). + ABS does not trap, so -fno-trapping-math is required while NaNs are + honored. A floating compare is not inverted into A >= 0 ? A : -A, + so these folds need their own patterns. */ + +float f0(float A) +{ +// A < 0 ? -A : A same as abs (A) + if (A < 0) return -A; + return A; +} +float f1(float A) +{ +// A <= 0 ? -A : A same as abs (A) + if (A <= 0) return -A; + return A; +} +float f2(float A) +{ +// A > 0 ? -A : A same as -abs (A) + if (A > 0) return -A; + return A; +} +float f3(float A) +{ +// A >= 0 ? -A : A same as -abs (A) + if (A >= 0) return -A; + return A; +} + +/* f2 and f3 are -abs, not allowed in early phi-opt. */ +/* { dg-final { scan-tree-dump-times "if" 2 "phiopt1" } } */ +/* { dg-final { scan-tree-dump-not "if" "phiopt2" } } */
