Fix folding of Inf/NaN comparisons for -ftrapping-math (PR tree-optimization/64811)

Joseph Myers joseph@codesourcery.com
Fri Jan 5 21:13:00 GMT 2018


The folding of comparisons against Inf (to constants or comparisons
with the maximum finite value) has various cases where it introduces
or loses "invalid" exceptions for comparisons with NaNs.

Folding x > +Inf to 0 should not be about HONOR_SNANS - ordered
comparisons of both quiet and signaling NaNs should raise invalid.

x <= +Inf is not the same as x == x, because again that loses an
exception (equality comparisons don't raise exceptions except for
signaling NaNs).

x == +Inf is not the same as x > DBL_MAX, and a similar issue applies
with the x != +Inf case - that transformation causes a spurious
exception.

This patch fixes the conditionals on the folding to avoid such
introducing or losing exceptions.

Bootstrapped with no regressions on x86_64-pc-linux-gnu (where the
cases involving spurious exceptions wouldn't have failed anyway before
GCC 8 because of unordered comparisons wrongly always having formerly
been used by the back end).  Also tested for powerpc-linux-gnu
soft-float that this fixes many glibc math/ test failures that arose
in that configuration because this folding affected the IBM long
double support in libgcc (no such failures appeared for hard-float
because of the bug of powerpc hard-float always using unordered
comparisons) - some failures remain, but I believe them to be
unrelated.  OK to commit?

gcc:
2018-01-05  Joseph Myers  <joseph@codesourcery.com>

	PR tree-optimization/64811
	* match.pd: When optimizing comparisons with Inf, avoid
	introducing or losing exceptions from comparisons with NaN.

gcc/testsuite:
2018-01-05  Joseph Myers  <joseph@codesourcery.com>

	PR tree-optimization/64811
	* gcc.dg/torture/inf-compare-1.c, gcc.dg/torture/inf-compare-2.c,
	gcc.dg/torture/inf-compare-3.c, gcc.dg/torture/inf-compare-4.c,
	gcc.dg/torture/inf-compare-5.c, gcc.dg/torture/inf-compare-6.c,
	gcc.dg/torture/inf-compare-7.c, gcc.dg/torture/inf-compare-8.c:
	New tests.
	* gcc.c-torture/execute/ieee/fp-cmp-7.x: New file.

Index: gcc/match.pd
===================================================================
--- gcc/match.pd	(revision 256279)
+++ gcc/match.pd	(working copy)
@@ -3050,18 +3050,22 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
          code = swap_tree_comparison (code);
      }
      (switch
-      /* x > +Inf is always false, if with ignore sNANs.  */
+      /* x > +Inf is always false, if we ignore NaNs or exceptions.  */
       (if (code == GT_EXPR
-	   && ! HONOR_SNANS (@0))
+	   && !(HONOR_NANS (@0) && flag_trapping_math))
        { constant_boolean_node (false, type); })
       (if (code == LE_EXPR)
-       /* x <= +Inf is always true, if we don't case about NaNs.  */
+       /* x <= +Inf is always true, if we don't care about NaNs.  */
        (if (! HONOR_NANS (@0))
 	{ constant_boolean_node (true, type); }
-	/* x <= +Inf is the same as x == x, i.e. !isnan(x).  */
-	(eq @0 @0)))
-      /* x == +Inf and x >= +Inf are always equal to x > DBL_MAX.  */
-      (if (code == EQ_EXPR || code == GE_EXPR)
+	/* x <= +Inf is the same as x == x, i.e. !isnan(x), but this loses
+	   an "invalid" exception.  */
+	(if (!flag_trapping_math)
+	 (eq @0 @0))))
+      /* x == +Inf and x >= +Inf are always equal to x > DBL_MAX, but
+	 for == this introduces an exception for x a NaN.  */
+      (if ((code == EQ_EXPR && !(HONOR_NANS (@0) && flag_trapping_math))
+	   || code == GE_EXPR)
        (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
 	(if (neg)
 	 (lt @0 { build_real (TREE_TYPE (@0), max); })
@@ -3072,7 +3076,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 	(if (neg)
 	 (ge @0 { build_real (TREE_TYPE (@0), max); })
 	 (le @0 { build_real (TREE_TYPE (@0), max); }))))
-      /* x != +Inf is always equal to !(x > DBL_MAX).  */
+      /* x != +Inf is always equal to !(x > DBL_MAX), but this introduces
+	 an exception for x a NaN so use an unordered comparison.  */
       (if (code == NE_EXPR)
        (with { real_maxval (&max, neg, TYPE_MODE (TREE_TYPE (@0))); }
 	(if (! HONOR_NANS (@0))
@@ -3080,10 +3085,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 	  (ge @0 { build_real (TREE_TYPE (@0), max); })
 	  (le @0 { build_real (TREE_TYPE (@0), max); }))
 	 (if (neg)
-	  (bit_xor (lt @0 { build_real (TREE_TYPE (@0), max); })
-	   { build_one_cst (type); })
-	  (bit_xor (gt @0 { build_real (TREE_TYPE (@0), max); })
-	   { build_one_cst (type); }))))))))))
+	  (unge @0 { build_real (TREE_TYPE (@0), max); })
+	  (unle @0 { build_real (TREE_TYPE (@0), max); }))))))))))
 
  /* If this is a comparison of a real constant with a PLUS_EXPR
     or a MINUS_EXPR of a real constant, we can convert it into a
Index: gcc/testsuite/gcc.c-torture/execute/ieee/fp-cmp-7.x
===================================================================
--- gcc/testsuite/gcc.c-torture/execute/ieee/fp-cmp-7.x	(nonexistent)
+++ gcc/testsuite/gcc.c-torture/execute/ieee/fp-cmp-7.x	(working copy)
@@ -0,0 +1,2 @@
+lappend additional_flags "-fno-trapping-math"
+return 0
Index: gcc/testsuite/gcc.dg/torture/inf-compare-1.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/inf-compare-1.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/torture/inf-compare-1.c	(working copy)
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target fenv_exceptions } */
+
+#include <fenv.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile double x = __builtin_nan ("");
+volatile int i;
+
+int
+main (void)
+{
+  i = x > __builtin_inf ();
+  if (i != 0 || !fetestexcept (FE_INVALID))
+    abort ();
+}
Index: gcc/testsuite/gcc.dg/torture/inf-compare-2.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/inf-compare-2.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/torture/inf-compare-2.c	(working copy)
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target fenv_exceptions } */
+
+#include <fenv.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile double x = __builtin_nan ("");
+volatile int i;
+
+int
+main (void)
+{
+  i = x < -__builtin_inf ();
+  if (i != 0 || !fetestexcept (FE_INVALID))
+    abort ();
+}
Index: gcc/testsuite/gcc.dg/torture/inf-compare-3.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/inf-compare-3.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/torture/inf-compare-3.c	(working copy)
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target fenv_exceptions } */
+
+#include <fenv.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile double x = __builtin_nan ("");
+volatile int i;
+
+int
+main (void)
+{
+  i = x <= __builtin_inf ();
+  if (i != 0 || !fetestexcept (FE_INVALID))
+    abort ();
+}
Index: gcc/testsuite/gcc.dg/torture/inf-compare-4.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/inf-compare-4.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/torture/inf-compare-4.c	(working copy)
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target fenv_exceptions } */
+
+#include <fenv.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile double x = __builtin_nan ("");
+volatile int i;
+
+int
+main (void)
+{
+  i = x >= -__builtin_inf ();
+  if (i != 0 || !fetestexcept (FE_INVALID))
+    abort ();
+}
Index: gcc/testsuite/gcc.dg/torture/inf-compare-5.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/inf-compare-5.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/torture/inf-compare-5.c	(working copy)
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target fenv_exceptions } */
+
+#include <fenv.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile double x = __builtin_nan ("");
+volatile int i;
+
+int
+main (void)
+{
+  i = x == __builtin_inf ();
+  if (i != 0 || fetestexcept (FE_INVALID))
+    abort ();
+}
Index: gcc/testsuite/gcc.dg/torture/inf-compare-6.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/inf-compare-6.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/torture/inf-compare-6.c	(working copy)
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target fenv_exceptions } */
+
+#include <fenv.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile double x = __builtin_nan ("");
+volatile int i;
+
+int
+main (void)
+{
+  i = x == -__builtin_inf ();
+  if (i != 0 || fetestexcept (FE_INVALID))
+    abort ();
+}
Index: gcc/testsuite/gcc.dg/torture/inf-compare-7.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/inf-compare-7.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/torture/inf-compare-7.c	(working copy)
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target fenv_exceptions } */
+
+#include <fenv.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile double x = __builtin_nan ("");
+volatile int i;
+
+int
+main (void)
+{
+  i = x != __builtin_inf ();
+  if (i != 1 || fetestexcept (FE_INVALID))
+    abort ();
+}
Index: gcc/testsuite/gcc.dg/torture/inf-compare-8.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/inf-compare-8.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/torture/inf-compare-8.c	(working copy)
@@ -0,0 +1,19 @@
+/* { dg-do run } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target fenv_exceptions } */
+
+#include <fenv.h>
+
+extern void abort (void);
+extern void exit (int);
+
+volatile double x = __builtin_nan ("");
+volatile int i;
+
+int
+main (void)
+{
+  i = x != -__builtin_inf ();
+  if (i != 1 || fetestexcept (FE_INVALID))
+    abort ();
+}

-- 
Joseph S. Myers
joseph@codesourcery.com



More information about the Gcc-patches mailing list