From: Kyrylo Tkachov <[email protected]>

A conditional comparison can skip its comparison operand.  This is not valid
when the source evaluates both comparisons and the skipped comparison can
trap.  For example:

  int
  f (double a, double b, double c, double d)
  {
    return (a < b) & (c < d);
  }

At -O2, AArch64 emitted:

        fcmpe   d0, d1
        fccmpe  d2, d3, 0, mi
        cset    w0, mi

If a < b is false, FCCMPE does not compare c and d.  A quiet NaN in c or d
therefore does not raise Invalid, although bitwise AND evaluates both
operands.  After this patch GCC emits:

        fcmpe   d0, d1
        cset    w1, mi
        fcmpe   d2, d3
        cset    w0, mi
        and     w0, w1, w0

Reject a potentially trapping comparison from every conditional position.
A trapping comparison can still lead the sequence because the first
comparison always executes.

The runtime test clears FPSR with the AArch64 builtins and verifies that an
ordered comparison with a quiet NaN raises Invalid.  The compile test covers
signalling NaNs.

Bootstrapped and tested on aarch64-none-linux-gnu.
Ok for trunk?
Thanks,
Kyrill

gcc/

        * ccmp.cc (expand_ccmp_next): Reject a comparison that can trap.

gcc/testsuite/

        * gcc.target/aarch64/fccmp-trap-1.c: New test.
        * gcc.target/aarch64/fccmp-trap-2.c: Likewise.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/ccmp.cc                                   |  6 +++++
 .../gcc.target/aarch64/fccmp-trap-1.c         | 25 +++++++++++++++++++
 .../gcc.target/aarch64/fccmp-trap-2.c         | 12 +++++++++
 3 files changed, 43 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/fccmp-trap-1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/fccmp-trap-2.c

diff --git a/gcc/ccmp.cc b/gcc/ccmp.cc
index 0564ccf34bf..f63e44dcc84 100644
--- a/gcc/ccmp.cc
+++ b/gcc/ccmp.cc
@@ -168,6 +168,12 @@ expand_ccmp_next (tree op, tree_code code, rtx prev,
   rtx_code rcode;
   tree rhs1, rhs2;
 
+  /* A conditional comparison can skip OP.  Only the first comparison can
+     therefore have observable traps.  */
+  gimple *g = get_gimple_for_ssa_name (op);
+  if (g && gimple_assign_rhs_could_trap_p (g))
+    return NULL_RTX;
+
   get_compare_parts (op, &rcode, &rhs1, &rhs2);
   return targetm.gen_ccmp_next (prep_seq, gen_seq, prev, rcode,
                                rhs1, rhs2, get_rtx_code (code, 0));
diff --git a/gcc/testsuite/gcc.target/aarch64/fccmp-trap-1.c 
b/gcc/testsuite/gcc.target/aarch64/fccmp-trap-1.c
new file mode 100644
index 00000000000..1d151f2f056
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fccmp-trap-1.c
@@ -0,0 +1,25 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+enum { FPSR_IOC = 1 << 0 };
+
+__attribute__ ((noipa))
+static int
+and_lt (double a, double b, double c, double d)
+{
+  return (a < b) & (c < d);
+}
+
+int
+main (void)
+{
+  double qnan = __builtin_nan ("");
+
+  __builtin_aarch64_set_fpsr (0);
+  if (and_lt (1.0, 0.0, qnan, 0.0) != 0)
+    __builtin_abort ();
+  if ((__builtin_aarch64_get_fpsr () & FPSR_IOC) == 0)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/fccmp-trap-2.c 
b/gcc/testsuite/gcc.target/aarch64/fccmp-trap-2.c
new file mode 100644
index 00000000000..4b48fe5b6aa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fccmp-trap-2.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fsignaling-nans" } */
+
+int
+and_eq (double a, double b, double c, double d)
+{
+  return (a == b) & (c == d);
+}
+
+/* Both comparisons can raise an exception for a signaling NaN.  */
+/* { dg-final { scan-assembler-not {\tfccmp} } } */
+/* { dg-final { scan-assembler-times {\tfcmp\td[0-9]+, d[0-9]+} 2 } } */
-- 
2.50.1 (Apple Git-155)

Reply via email to