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