[gcc r17-3158] PR tree-optimization/126467: 0.0-x -> -x vs. signed zeros in match.pd.

Roger Sayle via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
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;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.