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)