On Tue, Aug 11, 2026 at 11:33 PM <[email protected]> wrote: > > From: Kyrylo Tkachov <[email protected]> > > A conditional comparison does not compare its operands when the preceding > condition is false, so it does not raise the exception that the comparison > it replaces would raise. A bitwise operation evaluates both of its > operands, so that exception has to be preserved. For example, at -O2: > > int > f (double a, double b, double c, double d) > { > return (a < b) & (c < d); > } > > fcmpe d0, d1 > fccmpe d2, d3, 0, mi > cset w0, mi > > A quiet NaN in c or d does not raise Invalid when a < b is false. After > this patch: > > fcmpe d0, d1 > cset w1, mi > fcmpe d2, d3 > cset w0, mi > and w0, w1, w0 > > Reject such a comparison in aarch64_gen_ccmp_next. CCFPE marks a > comparison that raises Invalid for a quiet NaN, so it identifies the > comparisons that have to stay unconditional, and a signalling NaN raises > Invalid in every comparison. The rejection happens with the other early > exits, before the operands are expanded, which r16-5897 added to keep the > expansion of a long chain out of quadratic behaviour [PR99782]. Both a > short-circuit source and a chain > under -ffinite-math-only or -fno-trapping-math keep their conditional > comparisons: ifcombine only merges conditions that cannot trap, and > neither option makes the exception observable. > > The first comparison of a sequence always executes, so a trapping > comparison can still lead one. The expander already tries both orders of > a comparison pair, so a mixed sequence keeps the floating-point comparison > in front and folds the integer one. > > Document the rule in the gen_ccmp_next hook. The i386 implementation > already refuses every floating-point comparison in this position. > > gcc/ > > * config/aarch64/aarch64.cc (aarch64_gen_ccmp_next): Reject a > comparison that would lose an observable exception. > * target.def (gen_ccmp_next): Document the rule. > * doc/tm.texi: Regenerate.
Ok, once the other patch is finalized. > > gcc/testsuite/ > > * gcc.target/aarch64/fccmp-trap-1.c: New test. > * gcc.target/aarch64/fccmp-trap-2.c: Likewise. > * gcc.target/aarch64/fccmp-trap-3.c: Likewise. > > Signed-off-by: Kyrylo Tkachov <[email protected]> > --- > gcc/config/aarch64/aarch64.cc | 10 ++++ > gcc/doc/tm.texi | 3 ++ > gcc/target.def | 3 ++ > .../gcc.target/aarch64/fccmp-trap-1.c | 25 +++++++++ > .../gcc.target/aarch64/fccmp-trap-2.c | 54 +++++++++++++++++++ > .../gcc.target/aarch64/fccmp-trap-3.c | 14 +++++ > 6 files changed, 109 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 > create mode 100644 gcc/testsuite/gcc.target/aarch64/fccmp-trap-3.c > > diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc > index 0b73146b620..8a597be69f7 100644 > --- a/gcc/config/aarch64/aarch64.cc > +++ b/gcc/config/aarch64/aarch64.cc > @@ -29698,6 +29698,16 @@ aarch64_gen_ccmp_next (rtx_insn **prep_seq, rtx_insn > **gen_seq, rtx prev, > || op_mode == SFmode || op_mode == DFmode)) > return NULL_RTX; > > + /* A conditional comparison does not compare its operands when the > preceding > + condition is false, so it cannot raise the exception that the comparison > + it replaces would raise. CCFPE marks a comparison that raises Invalid > for > + a quiet NaN, and every comparison raises it for a signalling NaN. > Reject > + it here, before the operands are expanded. */ > + if (FLOAT_MODE_P (op_mode) > + && (aarch64_fp_cc_mode (cmp_code, op_mode) == CCFPEmode > + || HONOR_SNANS (op_mode))) > + return NULL_RTX; > + > push_to_sequence (*prep_seq); > expand_operands (treeop0, treeop1, NULL_RTX, &op0, &op1, EXPAND_NORMAL); > > diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi > index 85e17525683..6d5a77f2ea4 100644 > --- a/gcc/doc/tm.texi > +++ b/gcc/doc/tm.texi > @@ -12615,6 +12615,9 @@ This function prepares to emit a conditional > comparison within a sequence > or @code{gen_ccmp_next}. It may return @code{NULL} if the combination of > @var{prev} and this comparison is not supported, otherwise the result must > be appropriate for passing to @code{gen_ccmp_next} or @code{cbranch_optab}. > + The comparison is not performed when @var{prev} is false, so this function > + has to return @code{NULL} for a comparison that raises an exception which > + has to be preserved. > @var{code} is the @code{rtx_code} of the compare for @var{op0} and > @var{op1}. > @var{bit_code} is @code{AND} or @code{IOR}, which is the op on the compares. > @end deftypefn > diff --git a/gcc/target.def b/gcc/target.def > index 884fe1bd57e..995b0699307 100644 > --- a/gcc/target.def > +++ b/gcc/target.def > @@ -2843,6 +2843,9 @@ DEFHOOK > or @code{gen_ccmp_next}. It may return @code{NULL} if the combination of\n\ > @var{prev} and this comparison is not supported, otherwise the result > must\n\ > be appropriate for passing to @code{gen_ccmp_next} or > @code{cbranch_optab}.\n\ > + The comparison is not performed when @var{prev} is false, so this > function\n\ > + has to return @code{NULL} for a comparison that raises an exception which\n\ > + has to be preserved.\n\ > @var{code} is the @code{rtx_code} of the compare for @var{op0} and > @var{op1}.\n\ > @var{bit_code} is @code{AND} or @code{IOR}, which is the op on the > compares.", > rtx, (rtx_insn **prep_seq, rtx_insn **gen_seq, rtx prev, rtx_code cmp_code, > tree op0, tree op1, rtx_code bit_code), > 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..0f5fb75b922 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/aarch64/fccmp-trap-2.c > @@ -0,0 +1,54 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2" } */ > +/* { dg-final { check-function-bodies "**" "" } } */ > + > +/* A bitwise operation evaluates both comparisons, so a conditional > + comparison would hide the Invalid exception that the second ordered > + comparison raises for a quiet NaN. */ > + > +/* > +** and_lt: > +** fcmpe d0, d1 > +** cset w[0-9]+, mi > +** fcmpe d2, d3 > +** cset w[0-9]+, mi > +** and w0, w[0-9]+, w[0-9]+ > +** ret > +*/ > +int > +and_lt (double a, double b, double c, double d) > +{ > + return (a < b) & (c < d); > +} > + > +/* An equality comparison only raises Invalid for a signalling NaN, which > + this test does not honour, so it can still be made conditional. */ > + > +/* > +** and_eq: > +** fcmp d0, d1 > +** fccmp d2, d3, 0, eq > +** cset w0, eq > +** ret > +*/ > +int > +and_eq (double a, double b, double c, double d) > +{ > + return (a == b) & (c == d); > +} > + > +/* Only the floating-point comparison has to stay unconditional. The target > + rejects it in the conditional position, and the expander leads with it. > */ > + > +/* > +** and_int: > +** fcmpe d0, d1 > +** ccmp w0, w1, 0, mi > +** cset w0, lt > +** ret > +*/ > +int > +and_int (double a, double b, int i, int j) > +{ > + return (a < b) & (i < j); > +} > diff --git a/gcc/testsuite/gcc.target/aarch64/fccmp-trap-3.c > b/gcc/testsuite/gcc.target/aarch64/fccmp-trap-3.c > new file mode 100644 > index 00000000000..2004e435eb1 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/aarch64/fccmp-trap-3.c > @@ -0,0 +1,14 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fsignaling-nans" } */ > + > +/* Every comparison raises Invalid for a signalling NaN, so none of them can > + become conditional. */ > + > +int > +and_eq (double a, double b, double c, double d) > +{ > + return (a == b) & (c == d); > +} > + > +/* { dg-final { scan-assembler-times {\tfcmp\td[0-9]+, d[0-9]+} 2 } } */ > +/* { dg-final { scan-assembler-not {\tfccmpe?\t} } } */ > -- > 2.50.1 (Apple Git-155) >
