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) >
