On Mon, Aug 10, 2026 at 6:28 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> Only the first comparison in a conditional-compare sequence is plain.  On
> AArch64, FCMP accepts zero directly but FCCMP does not.  For example, with
> -O2 -ffinite-math-only:
>
>   int
>   f (double a, double b, double c, double d, double e)
>   {
>     return (e < 0.0) & (a < b) & (c < d);
>   }

I think Reassociation should do this instead of a secondary
Reassociation as part of ccmp.

>
> The normal expansion order materialises zero:
>
>         fcmp    d0, d1
>         movi    d31, #0
>         fccmp   d2, d3, 0, mi
>         fccmp   d4, d31, 0, mi
>         cset    w0, mi
>
> Putting the zero comparison first removes that materialisation:
>
>         fcmp    d4, #0.0
>         fccmp   d0, d1, 0, mi
>         fccmp   d2, d3, 0, mi
>         cset    w0, mi
>
> For a root that joins a constant leaf to a uniform tree, build one complete
> constant-first alternative.  Use it only if the normal order fails or the
> target costs show that the complete alternative is cheaper.  Keep the normal
> order when the costs are equal.  Trying at most one extra order keeps the
> additional target expansion linear and needs no arbitrary threshold.
>
> The tests also cover integer immediates and verify that an equal-cost small
> immediate does not displace a more useful large constant.
>
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Ok for trunk?
> Thanks,
> Kyrill
>
> gcc/
>
>         * ccmp.cc (expand_ccmp_constant_first): New function.
>         (expand_ccmp_expr): Cost one constant-first root alternative.
>
> gcc/testsuite/
>
>         * gcc.target/aarch64/ccmp_8.c: New test.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/ccmp.cc                               | 50 ++++++++++++++
>  gcc/testsuite/gcc.target/aarch64/ccmp_8.c | 80 +++++++++++++++++++++++
>  2 files changed, 130 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.target/aarch64/ccmp_8.c
>
> diff --git a/gcc/ccmp.cc b/gcc/ccmp.cc
> index 9f7670bf58e..00aa32f74a4 100644
> --- a/gcc/ccmp.cc
> +++ b/gcc/ccmp.cc
> @@ -374,6 +374,38 @@ expand_ccmp_expr_1 (gimple *g, rtx_insn **prep_seq, 
> rtx_insn **gen_seq,
>      }
>  }
>
> +/* Try to put a constant leaf of root G before its uniform tree operand.
> +   Return the resulting comparison and set PREP_SEQ and GEN_SEQ, or return
> +   NULL_RTX if the root has no such leaf or expansion fails.  */
> +
> +static rtx
> +expand_ccmp_constant_first (gimple *g, ccmp_uniform_cache &cache,
> +                           rtx_insn **prep_seq, rtx_insn **gen_seq)
> +{
> +  tree_code code = gimple_assign_rhs_code (g);
> +  basic_block bb = gimple_bb (g);
> +  tree leaf = gimple_assign_rhs1 (g);
> +  tree tree_op = gimple_assign_rhs2 (g);
> +  if (!ccmp_tree_comparison_p (leaf, bb))
> +    std::swap (leaf, tree_op);
> +  if (ccmp_tree_comparison_p (tree_op, bb))
> +    return NULL_RTX;
> +
> +  gimple *leaf_stmt = get_gimple_for_ssa_name (leaf);
> +  if (!leaf_stmt
> +      || !CONSTANT_CLASS_P (gimple_assign_rhs2 (leaf_stmt))
> +      || !ccmp_uniform_chain_p (tree_op, code, bb, cache))
> +    return NULL_RTX;
> +
> +  rtx_code rcode;
> +  tree rhs1, rhs2;
> +  get_compare_parts (leaf, &rcode, &rhs1, &rhs2);
> +  rtx prev = targetm.gen_ccmp_first (prep_seq, gen_seq, rcode, rhs1, rhs2);
> +  if (!prev)
> +    return NULL_RTX;
> +  return expand_ccmp_chain (tree_op, code, prev, prep_seq, gen_seq);
> +}
> +
>  /* Main entry to expand conditional compare statement G.
>     Return NULL_RTX if G is not a legal candidate or expand fail.
>     Otherwise return the target.  */
> @@ -397,6 +429,24 @@ expand_ccmp_expr (gimple *g, machine_mode mode)
>    rtx_insn *prep_seq = NULL, *gen_seq = NULL;
>    tmp = expand_ccmp_expr_1 (g, &prep_seq, &gen_seq, cache);
>
> +  rtx_insn *constant_prep = NULL, *constant_gen = NULL;
> +  rtx constant = expand_ccmp_constant_first (g, cache, &constant_prep,
> +                                            &constant_gen);
> +  if (constant)
> +    {
> +      int speed_p = optimize_insn_for_speed_p ();
> +      unsigned constant_cost = seq_cost (constant_prep, speed_p);
> +      constant_cost += seq_cost (constant_gen, speed_p);
> +      if (!tmp
> +         || constant_cost < (seq_cost (prep_seq, speed_p)
> +                             + seq_cost (gen_seq, speed_p)))
> +       {
> +         tmp = constant;
> +         prep_seq = constant_prep;
> +         gen_seq = constant_gen;
> +       }
> +    }
> +
>    if (tmp)
>      {
>        insn_code icode;
> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp_8.c 
> b/gcc/testsuite/gcc.target/aarch64/ccmp_8.c
> new file mode 100644
> index 00000000000..12fb0299e7d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/ccmp_8.c
> @@ -0,0 +1,80 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -ffinite-math-only" } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +/* A plain comparison accepts constants that a conditional comparison can
> +   reject.  Compare the complete sequence costs before moving a constant
> +   comparison ahead of a uniform chain.  */
> +
> +/*
> +** fp_first:
> +**     fcmp    d4, #0\.0
> +**     fccmp   d0, d1, 0, mi
> +**     fccmp   d2, d3, 0, mi
> +**     cset    w0, mi
> +**     ret
> +*/
> +int
> +fp_first (double a, double b, double c, double d, double e)
> +{
> +  return (e < 0.0) & (a < b) & (c < d);
> +}
> +
> +/*
> +** fp_last:
> +**     fcmp    d4, #0\.0
> +**     fccmp   d2, d3, 0, mi
> +**     fccmp   d0, d1, 0, mi
> +**     cset    w0, mi
> +**     ret
> +*/
> +int
> +fp_last (double a, double b, double c, double d, double e)
> +{
> +  return (a < b) & (c < d) & (e < 0.0);
> +}
> +
> +/*
> +** int_first:
> +**     cmp     w4, 100
> +**     ccmp    w0, w1, 0, eq
> +**     ccmp    w2, w3, 0, lt
> +**     cset    w0, lt
> +**     ret
> +*/
> +int
> +int_first (int a, int b, int c, int d, int e)
> +{
> +  return (e == 100) & (a < b) & (c < d);
> +}
> +
> +/*
> +** int_last:
> +**     cmp     w4, 100
> +**     ccmp    w2, w3, 0, eq
> +**     ccmp    w0, w1, 0, lt
> +**     cset    w0, lt
> +**     ret
> +*/
> +int
> +int_last (int a, int b, int c, int d, int e)
> +{
> +  return (a < b) & (c < d) & (e == 100);
> +}
> +
> +/* Keep the large constant first when the root leaf is a cheaper CCMP
> +   immediate.  */
> +
> +/*
> +** int_two_constants:
> +**     cmp     w0, 100
> +**     ccmp    w1, w2, 0, eq
> +**     ccmp    w3, 3, 0, lt
> +**     cset    w0, eq
> +**     ret
> +*/
> +int
> +int_two_constants (int a, int b, int c, int d)
> +{
> +  return (a == 100) & (b < c) & (d == 3);
> +}
> --
> 2.50.1 (Apple Git-155)
>

Reply via email to