Ping.

https://gcc.gnu.org/pipermail/gcc-patches/2026-August/727173.html
Thanks,
Kyrill


> On 11 Aug 2026, at 12:42, Kyrylo Tkachov <[email protected]> wrote:
> 
> From: Kyrylo Tkachov <[email protected]>
> 
> Only the first comparison of a conditional compare sequence is a plain
> comparison.  It is the only one that a target cannot refuse and the only
> one that uses the immediate range of a plain comparison, so the operand
> order of the chain decides whether the sequence forms at all and how many
> instructions it needs.  Reassociation orders the chain by rank, which does
> not know any of this.  For
> 
>  int
>  f (int a, int b, int c, int d, int e)
>  {
>    return (e == 100) & (a < b) & (c < d);
>  }
> 
> AArch64 emitted a sequence that materialises the constant:
> 
> cmp w0, w1
> mov w0, 100
> ccmp w2, w3, 0, lt
> ccmp w4, w0, 0, lt
> cset w0, eq
> 
> rewrite_expr_tree builds the chain from the last two operands of the
> operand list, and the expander already compares the cost of both orders of
> that leading pair.  Sort the operands of a conditional compare chain so
> that the ones that gain from leading it end up there.  An operand that is
> a sequence of the other code has to lead, a floating-point comparison can
> be refused in a conditional position, and a comparison against a constant
> is cheaper in front:
> 
> cmp w4, 100
> ccmp w2, w3, 0, eq
> ccmp w0, w1, 0, lt
> cset w0, lt
> 
> The order of the leading pair stays with the expander, which keeps the
> larger of two constants in front when only one of them fits a conditional
> comparison.
> 
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Ok for trunk?
> Thanks,
> Kyrill
> 
> gcc/
> 
> * tree-ssa-reassoc.cc (ccmp_head_preference): New function.
> (ccmp_compare_ops): Likewise.
> (reassociate_bb): Sort the operands of a conditional compare chain.
> 
> gcc/testsuite/
> 
> * gcc.target/aarch64/ccmp_8.c: New test.
> * gcc.target/aarch64/fccmp_4.c: Likewise.
> * gcc.target/aarch64/fccmp_5.c: Likewise.
> 
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
> gcc/testsuite/gcc.target/aarch64/ccmp_8.c  | 71 ++++++++++++++++++++++
> gcc/testsuite/gcc.target/aarch64/fccmp_4.c | 20 ++++++
> gcc/testsuite/gcc.target/aarch64/fccmp_5.c | 35 +++++++++++
> gcc/tree-ssa-reassoc.cc                    | 45 ++++++++++++++
> 4 files changed, 171 insertions(+)
> create mode 100644 gcc/testsuite/gcc.target/aarch64/ccmp_8.c
> create mode 100644 gcc/testsuite/gcc.target/aarch64/fccmp_4.c
> create mode 100644 gcc/testsuite/gcc.target/aarch64/fccmp_5.c
> 
> 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..08d6b1c07ea
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/ccmp_8.c
> @@ -0,0 +1,71 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +/* Only the leading comparison of a conditional compare sequence is a plain
> +   comparison, which accepts constants that CCMP rejects.  */
> +
> +/*
> +** int_head:
> +** cmp w4, 100
> +** ccmp w2, w3, 0, eq
> +** ccmp w0, w1, 0, lt
> +** cset w0, lt
> +** ret
> +*/
> +int
> +int_head (int a, int b, int c, int d, int e)
> +{
> +  return (e == 100) & (a < b) & (c < d);
> +}
> +
> +/* Both comparisons against a constant reach the leading pair, so the
> +   expander can compare their costs and lead with the constant that CCMP
> +   cannot encode.  */
> +
> +/*
> +** int_two_constants:
> +** cmp w0, 100
> +** ccmp w3, 3, 0, eq
> +** ccmp w1, w2, 0, eq
> +** cset w0, lt
> +** ret
> +*/
> +int
> +int_two_constants (int a, int b, int c, int d)
> +{
> +  return (a == 100) & (b < c) & (d == 3);
> +}
> +
> +/* An operand that is a sequence of the other code cannot be conditional, so
> +   it has to lead.  */
> +
> +/*
> +** and_of_ior:
> +** cmp w0, w1
> +** ccmp w2, w3, 1, ge
> +** ccmp w6, w7, 0, lt
> +** ccmp w4, w5, 0, lt
> +** cset w0, lt
> +** ret
> +*/
> +int
> +and_of_ior (int a, int b, int c, int d, int e, int f, int g, int h)
> +{
> +  return ((a < b) | (c < d)) & ((e < f) & (g < h));
> +}
> +
> +/*
> +** ior_of_and:
> +** cmp w0, w1
> +** ccmp w2, w3, 0, lt
> +** ccmp w6, w7, 1, ge
> +** ccmp w4, w5, 1, ge
> +** cset w0, lt
> +** ret
> +*/
> +int
> +ior_of_and (int a, int b, int c, int d, int e, int f, int g, int h)
> +{
> +  return ((a < b) & (c < d)) | ((e < f) | (g < h));
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/fccmp_4.c 
> b/gcc/testsuite/gcc.target/aarch64/fccmp_4.c
> new file mode 100644
> index 00000000000..b63e14f4d49
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/fccmp_4.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -ffinite-math-only" } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +/* FCMP compares against zero, FCCMP does not.  A comparison against a
> +   constant therefore has to reach the head of the sequence.  */
> +
> +/*
> +** fp_zero_head:
> +** fcmp d4, #0\.0
> +** fccmp d2, d3, 0, mi
> +** fccmp d0, d1, 0, mi
> +** cset w0, mi
> +** ret
> +*/
> +int
> +fp_zero_head (double a, double b, double c, double d, double e)
> +{
> +  return (e < 0.0) & (a < b) & (c < d);
> +}
> diff --git a/gcc/testsuite/gcc.target/aarch64/fccmp_5.c 
> b/gcc/testsuite/gcc.target/aarch64/fccmp_5.c
> new file mode 100644
> index 00000000000..4d141be171e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/fccmp_5.c
> @@ -0,0 +1,35 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +/* { dg-final { check-function-bodies "**" "" } } */
> +
> +/* An ordered floating-point comparison keeps its exception, so it cannot be
> +   made conditional here.  Leading the sequence with it keeps the integer
> +   comparisons in one chain.  */
> +
> +/*
> +** mixed:
> +** fcmpe d0, d1
> +** ccmp w2, w3, 0, mi
> +** ccmp w0, w1, 0, lt
> +** cset w0, lt
> +** ret
> +*/
> +int
> +mixed (double a, double b, int i, int j, int k, int l)
> +{
> +  return (a < b) & (i < j) & (k < l);
> +}
> +
> +/*
> +** mixed_rev:
> +** fcmpe d0, d1
> +** ccmp w2, w3, 0, mi
> +** ccmp w0, w1, 0, lt
> +** cset w0, lt
> +** ret
> +*/
> +int
> +mixed_rev (double a, double b, int i, int j, int k, int l)
> +{
> +  return (i < j) & (k < l) & (a < b);
> +}
> diff --git a/gcc/tree-ssa-reassoc.cc b/gcc/tree-ssa-reassoc.cc
> index d424dff7406..b5f47df516b 100644
> --- a/gcc/tree-ssa-reassoc.cc
> +++ b/gcc/tree-ssa-reassoc.cc
> @@ -5243,6 +5243,45 @@ ccmp_chain_p (const vec<operand_entry *> &ops, 
> tree_code code, basic_block bb)
>   return seen_foldable;
> }
> 
> +/* Return how much operand OP of a conditional compare chain gains from
> +   leading the sequence.  Only the leading comparison is unconditional, so
> +   it is the one that a target cannot refuse and the one that can use the
> +   immediate range of a plain comparison.  A sequence of its own therefore
> +   has to lead, a floating-point comparison can be refused in a conditional
> +   position, and a comparison against a constant is cheaper there.  */
> +
> +static int
> +ccmp_head_preference (tree op)
> +{
> +  gimple *def = SSA_NAME_DEF_STMT (op);
> +
> +  /* ccmp_chain_p only accepts comparisons and sequences of them.  */
> +  gcc_checking_assert (is_gimple_assign (def));
> +
> +  if (TREE_CODE_CLASS (gimple_assign_rhs_code (def)) != tcc_comparison)
> +    return 4;
> +
> +  int preference = 0;
> +  if (FLOAT_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (def))))
> +    preference += 2;
> +  if (CONSTANT_CLASS_P (gimple_assign_rhs2 (def)))
> +    preference += 1;
> +
> +  return preference;
> +}
> +
> +/* Compare two operands of a conditional compare chain by how much they gain
> +   from leading the sequence.  */
> +
> +static int
> +ccmp_compare_ops (const void *pa, const void *pb, void *)
> +{
> +  const operand_entry *oea = *(const operand_entry *const *) pa;
> +  const operand_entry *oeb = *(const operand_entry *const *) pb;
> +
> +  return ccmp_head_preference (oea->op) - ccmp_head_preference (oeb->op);
> +}
> +
> /* If definition of RHS1 or RHS2 dominates STMT, return the later of those
>    two definitions, otherwise return STMT.  Sets INSERT_BEFORE to indicate
>    whether RHS1 op RHS2 can be inserted before or needs to be inserted
> @@ -7276,6 +7315,12 @@ reassociate_bb (basic_block bb)
>       > 1)))
> swap_ops_for_binary_stmt (ops, len - 3);
> 
> +      /* rewrite_expr_tree starts the chain with the last two
> + operands, so put the operands that gain from leading
> + a conditional compare sequence there.  */
> +      if (!reassoc_insert_powi_p && ccmp_chain)
> + ops.stablesort (ccmp_compare_ops, NULL);
> +
>      new_lhs = rewrite_expr_tree (stmt, rhs_code, 0, ops,
>   powi_result != NULL
>   || negate_result,
> -- 
> 2.50.1 (Apple Git-155)
> 

Reply via email to