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