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)