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