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.
This patch has been tested on x86_64-pc-linux-gnu with make bootstrap
and make -k check, both with and without --target_board=unix{-m32}
with no new failures. Ok for mainline?
2026-08-05 Roger Sayle <[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.
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 --git a/gcc/match.pd b/gcc/match.pd
index 536d5125a0b..b69543f35f2 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -5910,12 +5910,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 ((!HONOR_NANS (type) || !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 00000000000..b078b436454
--- /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 00000000000..28742553173
--- /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 fb7de217f96..82756907f3f 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;