https://gcc.gnu.org/g:2fb55e1d061ab091beafa04b2050c88f8d8d64bf
commit r17-3158-g2fb55e1d061ab091beafa04b2050c88f8d8d64bf Author: Roger Sayle <[email protected]> Date: Sun Aug 9 08:00:06 2026 +0200 PR tree-optimization/126467: 0.0-x -> -x vs. signed zeros in match.pd. This is my proposed solution to PR tree-optimization/126467, where we're inappropriately converting 0.0 - x to -x when we honor IEEE signed zeros. This transformation is valid with -Ofast, but by default +0.0 - +0.0 should return +0.0, but -(+0.0) is -0.0. Likewise when x is NaN, 0.0 - x may change the payload, but -x is guaranteed not to. My fix is to separate the logic for this transformation from that for FP addition. Technically, we could do slightly better by introducing a tree_expr_negative_p (complementing and mutually recursive with the existing tree_expr_nonnegative_p), but that's a bigger change and less suitable for backporting to release branches, i.e. a follow-up. I agree with Alexander Monakov that an alternate fix might be to correctly reuse the existing fold_real_zero_addition_p functionality by constructing and garbage collecting a NEGATE_EXPR tree on each call, but this seems a little less efficient. 2026-08-09 Roger Sayle <[email protected]> Andrea Pinski <[email protected]> gcc/ChangeLog PR tree-optimization/126467 * match.pd (0.0 - x -> -x): Update the conditions under which the transformation is performed, disallowing x = +0.0 when we honor signed zeros. This is still disallowed if x is a NaN. gcc/testsuite/ChangeLog PR tree-optimization/126467 * gcc.dg/pr126467-1.c: New test case. * gcc.dg/pr126467-2.c: Likewise. * gcc.dg/pr96392.c: Fix incorrect test case. Diff: --- gcc/match.pd | 16 ++++++++++++---- gcc/testsuite/gcc.dg/pr126467-1.c | 21 +++++++++++++++++++++ gcc/testsuite/gcc.dg/pr126467-2.c | 15 +++++++++++++++ gcc/testsuite/gcc.dg/pr96392.c | 5 ----- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/gcc/match.pd b/gcc/match.pd index 993faff2be33..1ea46fd17264 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -6001,12 +6001,20 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (pointer_diff integer_zerop @1) (negate (convert @1))) -/* (ARG0 - ARG1) is the same as (-ARG1 + ARG0). So check whether - ARG0 is zero and X + ARG0 reduces to X, since that would mean - (-ARG1 + ARG0) reduces to -ARG1. */ +/* (0.0 - ARG1) can be transformed to -ARG1, if we don't honor NaNs + and signed zeros or ARG1 is known to be non-zero. Subtraction of + NaN may signal or modify payload, but negation doesn't, so it is + unsafe to apply this transformation for any kind of NaN. When + ARG1 is +0.0 or -0.0, the behaviour depends upon the rounding + mode. With the default rounding mode, (-0.0 - ARG1) is -ARG1, + but (+0.0 - ARG1) is only -ARG1 if ARG1 cannot be +0.0. */ (simplify (minus real_zerop@0 @1) - (if (fold_real_zero_addition_p (type, @1, @0, 0)) + (if (!tree_expr_maybe_nan_p (@1) + && (!HONOR_SIGNED_ZEROS (type) + || tree_expr_nonzero_p (@1) + || (!flag_rounding_math + && REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (@0))))) (negate @1))) /* Transform x * -1 into -x. */ diff --git a/gcc/testsuite/gcc.dg/pr126467-1.c b/gcc/testsuite/gcc.dg/pr126467-1.c new file mode 100644 index 000000000000..b078b4364544 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr126467-1.c @@ -0,0 +1,21 @@ +/* PR tree-optimization/126467 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-add-options ieee } */ + +/* 0.0 - x is not -x. + For x == +0.0, +0.0 - +0.0 is +0.0, but -x is -0.0. + Likewise fabs/negate preserve a NaN's payload but + subtraction doesn't. */ + +double foo (double x) +{ + return 0.0 - x; +} + +double bar (double y) +{ + return 0.0 - __builtin_fabs (y); +} + +/* { dg-final { scan-tree-dump-times " \\- " 2 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/pr126467-2.c b/gcc/testsuite/gcc.dg/pr126467-2.c new file mode 100644 index 000000000000..287425531736 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr126467-2.c @@ -0,0 +1,15 @@ +/* PR tree-optimization/126467 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fno-signed-zeros -ffinite-math-only -fdump-tree-optimized" } */ + +double foo (double x) +{ + return 0.0 - x; +} + +double bar (double y) +{ + return 0.0 - __builtin_fabs (y); +} + +/* { dg-final { scan-tree-dump-not " \\- " "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/pr96392.c b/gcc/testsuite/gcc.dg/pr96392.c index fb7de217f966..82756907f3ff 100644 --- a/gcc/testsuite/gcc.dg/pr96392.c +++ b/gcc/testsuite/gcc.dg/pr96392.c @@ -12,11 +12,6 @@ double sub0(int x) return x - 0.0; } -double negate(int x) -{ - return 0.0 - x; -} - double subtract(int x) { return (double)x - (double)x;
