Ping.

https://gcc.gnu.org/pipermail/gcc-patches/2026-August/727264.html

Thanks,
Kyrill

> On 12 Aug 2026, at 08:32, Kyrylo Tkachov <[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.
> 
> 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)
> 

Reply via email to