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