On Tue, Aug 11, 2026 at 8:20 AM Andrea Pinski
<[email protected]> wrote:
>
> On Tue, Aug 11, 2026 at 3:43 AM <[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.  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.
> >
> > Bootstrapped and tested on aarch64-none-linux-gnu.
> > Ok for trunk?
> > Thanks,
> > Kyrill
> >
> > 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.
> >
> > 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 e94b583d1bd..68e5aca9018 100644
> > --- a/gcc/config/aarch64/aarch64.cc
> > +++ b/gcc/config/aarch64/aarch64.cc
> > @@ -29720,6 +29720,16 @@ aarch64_gen_ccmp_next (rtx_insn **prep_seq, 
> > rtx_insn **gen_seq, rtx prev,
> >      case E_DFmode:
> >        cmp_mode = op_mode;
> >        cc_mode = aarch64_select_cc_mode (cmp_code, op0, op1);
> > +      /* 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.  */
> > +      if (cc_mode == CCFPEmode || HONOR_SNANS (op_mode))
> > +       {
> > +         end_sequence ();
> > +         return NULL_RTX;
> > +       }
>
> The other thing I should note is that we should check if
> treeop0/treeop1 can have a NAN or not; it would be a good idea to pass
> that down to aarch64_select_cc_mode.
> That should allow for more acceptance here.  Note this can be a future patch.
>
> Ok otherwise.

Actually, wait. This can expose some O(n^2) compile time behavior
because you reject after calling expand_operands.
See the comment towards the beginning of the function and
r16-5897-ge148a21f38327f .

Thanks,
Andrea

>
> >        break;
> >
> >      default:
> > 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)
> >

Reply via email to