On Mon, Aug 10, 2026 at 6:24 AM <[email protected]> wrote: > > 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.
I think it should be the target that rejects it and not the middle-end. I think x86_64 rejects floating point types for ccmp_next already though. > > 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) >
