LTGT is a signaling comparison and UNEQ is a quiet one, so reversing
one into the other changes whether a quiet NaN operand raises
FE_INVALID.  reversed_comparison_code_parts nevertheless reverses them
unconditionally.  Return UNKNOWN for them when flag_trapping_math is
set and the operands may be NaNs (or the comparison is in a MODE_CC
mode, which does not tell), mirroring what invert_tree_comparison does
at the GIMPLE level; keep the reversal otherwise.

That change alone exposes a latent problem in emit_store_flag: it
splits x != y into x unord y || x ltgt y, emitting a store-flag for
UNORDERED and a conditional move on LTGT.  The conditional move
evaluates its comparison unconditionally, so for a quiet != the LTGT
raises FE_INVALID on a NaN operand.  Today this is hidden only because
emit_conditional_move happens to reverse LTGT into the quiet UNEQ when
canonicalizing its arms; with LTGT no longer reversible, x86 emits a
signaling comisd for a quiet !=.  Use the equivalent
x unord y || !(x uneq y) directly, as do_compare_rtx_and_jump already
does for jumps, and no longer treat LTGT as a non-trapping remainder.
The NE split in do_compare_rtx_and_jump itself is safe: the UNORDERED
jump comes first, so LTGT only ever sees ordered operands.

        PR middle-end/122307

gcc/ChangeLog:

        * jump.cc (reversed_comparison_code_parts): Do not reverse LTGT
        and UNEQ if traps are honored and NaNs are possible.
        * expmed.cc (emit_store_flag): Split NE as UNORDERED || !UNEQ
        instead of UNORDERED || LTGT; do not treat LTGT as non-trapping.

gcc/testsuite/ChangeLog:

        * gcc.dg/torture/pr122307.c: New test.

Signed-off-by: Igor Shevlyakov <[email protected]>
---
Bootstrapped and regtested on x86_64-pc-linux-gnu (-m32/-m64), no new 
regressions.

 gcc/expmed.cc                           | 18 +++++++-
 gcc/jump.cc                             | 12 +++++-
 gcc/testsuite/gcc.dg/torture/pr122307.c | 56 +++++++++++++++++++++++++
 3 files changed, 82 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/torture/pr122307.c

diff --git a/gcc/expmed.cc b/gcc/expmed.cc
index b82c0bb10db..e029e67a729 100644
--- a/gcc/expmed.cc
+++ b/gcc/expmed.cc
@@ -6322,8 +6322,19 @@ emit_store_flag (rtx target, enum rtx_code code, rtx 
op0, rtx op1,
       if (!HAVE_conditional_move)
        return 0;
 
-      /* Do not turn a trapping comparison into a non-trapping one.  */
-      if ((code != EQ && code != NE && code != UNEQ && code != LTGT)
+      /* x != y is split into x unord y || x ltgt y, but the conditional move
+        below evaluates its comparison unconditionally and LTGT is signaling.
+        Use the quiet x unord y || !(x uneq y) instead.  */
+      bool invert = false;
+      if (code == LTGT && !and_them)
+       {
+         code = UNEQ;
+         invert = true;
+       }
+
+      /* Do not change whether the comparison traps: only the quiet
+        EQ, NE and UNEQ remainders come from quiet comparisons.  */
+      if ((code != EQ && code != NE && code != UNEQ)
          && flag_trapping_math)
        return 0;
 
@@ -6337,6 +6348,9 @@ emit_store_flag (rtx target, enum rtx_code code, rtx op0, 
rtx op1,
       if (and_them)
        tem = emit_conditional_move (target, { code, op0, op1, mode },
                                     tem, const0_rtx, GET_MODE (tem), 0);
+      else if (invert)
+       tem = emit_conditional_move (target, { code, op0, op1, mode },
+                                    tem, trueval, GET_MODE (tem), 0);
       else
        tem = emit_conditional_move (target, { code, op0, op1, mode },
                                     trueval, tem, GET_MODE (tem), 0);
diff --git a/gcc/jump.cc b/gcc/jump.cc
index 75158924c8f..21f5a313bd9 100644
--- a/gcc/jump.cc
+++ b/gcc/jump.cc
@@ -388,11 +388,19 @@ reversed_comparison_code_parts (enum rtx_code code, 
const_rtx arg0,
       return reverse_condition (code);
     case ORDERED:
     case UNORDERED:
-    case LTGT:
-    case UNEQ:
       /* In case we already see unordered comparison, we can be sure to
         be dealing with floating point so we don't need any more tests.  */
       return reverse_condition_maybe_unordered (code);
+    case LTGT:
+    case UNEQ:
+      /* LTGT is signaling and UNEQ is quiet, so reversing one into the
+        other changes whether a quiet NaN operand raises an exception.
+        That only matters if FP traps are honored and the operands may
+        be NaNs; a MODE_CC comparison does not tell us the latter.  */
+      if (flag_trapping_math
+         && (GET_MODE_CLASS (mode) == MODE_CC || HONOR_NANS (mode)))
+       return UNKNOWN;
+      return reverse_condition_maybe_unordered (code);
     case UNLT:
     case UNLE:
     case UNGT:
diff --git a/gcc/testsuite/gcc.dg/torture/pr122307.c 
b/gcc/testsuite/gcc.dg/torture/pr122307.c
new file mode 100644
index 00000000000..f4f5448e96c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/pr122307.c
@@ -0,0 +1,56 @@
+/* PR middle-end/122307: a quiet != (and ==) must not raise FE_INVALID
+   for a quiet NaN operand, in any expansion form.  */
+/* { dg-do run } */
+/* { dg-require-effective-target fenv_exceptions } */
+/* { dg-options "-O2" } */
+/* { dg-add-options ieee } */
+
+#include <fenv.h>
+
+#define NI __attribute__ ((noipa))
+
+#define GEN(T, S) \
+  NI int ne_##S (T a, T b) { return a != b; } \
+  NI int eq_##S (T a, T b) { return a == b; } \
+  NI int ne_sel_##S (T a, T b, int x, int y) { return a != b ? x : y; } \
+  NI int eq_sel_##S (T a, T b, int x, int y) { return a == b ? x : y; } \
+  NI int lg_##S (T a, T b) { return __builtin_islessgreater (a, b); } \
+  NI int ueq_##S (T a, T b) { return !__builtin_islessgreater (a, b); }
+
+GEN (float, f)
+GEN (double, d)
+
+#define CHECK(E, V) \
+  do { \
+    feclearexcept (FE_INVALID); \
+    if ((E) != (V) || fetestexcept (FE_INVALID)) \
+      __builtin_abort (); \
+  } while (0)
+
+int
+main (void)
+{
+  volatile float nf = __builtin_nanf (""), of = 1.0f;
+  volatile double nd = __builtin_nan (""), od = 1.0;
+
+  CHECK (ne_f (nf, of), 1);
+  CHECK (ne_f (of, nf), 1);
+  CHECK (eq_f (nf, of), 0);
+  CHECK (ne_sel_f (nf, of, 5, 7), 5);
+  CHECK (eq_sel_f (nf, of, 5, 7), 7);
+  CHECK (lg_f (nf, of), 0);
+  CHECK (ueq_f (nf, of), 1);
+
+  CHECK (ne_d (nd, od), 1);
+  CHECK (ne_d (od, nd), 1);
+  CHECK (eq_d (nd, od), 0);
+  CHECK (ne_sel_d (nd, od, 5, 7), 5);
+  CHECK (eq_sel_d (nd, od, 5, 7), 7);
+  CHECK (lg_d (nd, od), 0);
+  CHECK (ueq_d (nd, od), 1);
+
+  /* Ordered operands still compare correctly.  */
+  CHECK (ne_d (od, 2.0), 1);
+  CHECK (ne_d (od, od), 0);
+  return 0;
+}
-- 
2.55.0


Reply via email to