Hi,

Please find the patch proposed to handle some more cases for spaceship
optimization below.

Bootstrapped and regtested on powerpc64le-linux-gnu without any regressions.

Requesting review and suggestions on the test cases.

I do have one test file with 24 tests but did not send it with this patch
since currently in test cases there is no way of checking if target supports
spaceship optab, would it be ok to add check_effective_target_spaceship in
target-supports.exp? Or is there a  better way to add these test cases?

Following is the list of 24 tests:

Following 8 are recognized in current trunk.
a == b ? 0 : a > b ? 1 : -1
a == b ? 0 : a < b ? -1 : 1
a == b ? 0 : a >= b ? 1 : -1
a == b ? 0 : a <= b ? -1 : 1
a != b ? (a > b ? 1 : -1) : 0
a != b ? (a < b ? -1 : 1) : 0
a != b ? (a >= b ? 1 : -1) : 0
a != b ? (a <= b ? -1 : 1) : 0

Below tests are recognized with the patch.
a < b ? -1 : a > b ? 1 : 0
a < b ? -1 : a == b ? 0 : 1
a < b ? -1 : a != b ? 1 : 0
a < b ? -1 : a <= b ? 0 : 1
a > b ? 1 : a == b ? 0 : -1
a > b ? 1 : a < b ? -1 : 0
a > b ? 1 : a != b ? -1 : 0
a > b ? 1 : a >= b ? 0 : -1
a <= b ? (a == b ? 0 : -1) : 1
a <= b ? (a >= b ? 0 : -1) : 1
a <= b ? (a != b ? -1 : 0) : 1
a <= b ? (a < b ? -1 : 0) : 1
a >= b ? (a == b ? 0 : 1) : -1
a >= b ? (a != b ? 1 : 0) : -1
a >= b ? (a <= b ? 0 : 1) : -1
a >= b ? (a > b ? 1 : 0) : -1

Thanks and regards,
Avinash Jayakar

There are certain patterns that are not recognized by the method
optimize_spaceship. For example,
  a == b ? 0 : (a > b) : 1 : -1;
is rightly recognized as spaceship operator. But writing it as
  a <= b ? -(a < b) : 1 or
  a >= b ? (a > b) : -1
is not being currently recognized.

This patch recognizes such patterns and chooses to emit the spaceship
optab if target supports it, which improves code-generation for such
targets.

2026-02-23  Avinash Jayakar  <[email protected]>

gcc/ChangeLog:
        * tree-ssa-math-opts.cc (optimize_spaceship_for_ge_le): New
        function.
        (math_opts_dom_walker::after_dom_children): Use new fn, before
        running optimize_spaceship.
---
 gcc/tree-ssa-math-opts.cc | 162 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 161 insertions(+), 1 deletion(-)

diff --git a/gcc/tree-ssa-math-opts.cc b/gcc/tree-ssa-math-opts.cc
index bb67dca560b..a52118fafba 100644
--- a/gcc/tree-ssa-math-opts.cc
+++ b/gcc/tree-ssa-math-opts.cc
@@ -6096,6 +6096,165 @@ convert_mult_to_highpart (gassign *stmt, 
gimple_stmt_iterator *gsi)
   return true;
 }
 
+static bool
+optimize_spaceship_for_ge_le (gcond *stmt)
+{
+  /* Check condition form: a >= b or a <= b  */
+  enum tree_code code = gimple_cond_code (stmt);
+  if (code != GE_EXPR && code != LE_EXPR)
+    return false;
+
+  tree arg1 = gimple_cond_lhs (stmt);
+  tree arg2 = gimple_cond_rhs (stmt);
+
+  if (!INTEGRAL_TYPE_P (TREE_TYPE (arg1))
+      || optab_handler (spaceship_optab,
+                       TYPE_MODE (TREE_TYPE (arg1))) == CODE_FOR_nothing
+      || operand_equal_p (arg1, arg2, 0))
+    return false;
+
+  basic_block bb0 = gimple_bb (stmt);
+
+  /* Identify successors */
+  edge e_true = EDGE_SUCC (bb0, 0);
+  edge e_false = EDGE_SUCC (bb0, 1);
+
+  if (!(e_true->flags & EDGE_TRUE_VALUE))
+    std::swap (e_true, e_false);
+
+  basic_block bb1 = e_true->dest;
+  basic_block bb2 = e_false->dest;
+
+  // bb1 must be single-pred, single-succ
+  if (!single_pred_p (bb1) || !single_succ_p (bb1))
+    return false;
+
+  if (single_succ (bb1) != bb2)
+    return false;
+
+  gimple_stmt_iterator gsi = gsi_start_bb (bb1);
+
+  // if there is no stmt in bb1, no opt
+  if (gsi_end_p (gsi))
+    return false;
+
+  // first stmt in the bb must be a comparison
+  // NE_EXPR works for both GE and LE
+  // else if GE, then true block must have GT
+  // if LE, then true block must have LT
+  gassign *cmp = dyn_cast <gassign *> (gsi_stmt (gsi));
+  if (!cmp
+      || (gimple_assign_rhs_code (cmp) != NE_EXPR
+         && ((code == GE_EXPR && gimple_assign_rhs_code (cmp) != GT_EXPR)
+             || (code == LE_EXPR && gimple_assign_rhs_code (cmp) != LT_EXPR))))
+    return false;
+
+  // arguments participating in comparison must be same in original compare
+  if (!operand_equal_p (gimple_assign_rhs1 (cmp), arg1, 0)
+      || !operand_equal_p (gimple_assign_rhs2 (cmp), arg2, 0))
+    return false;
+
+  // grab the value of comparison is a ssa value
+  tree booltmp = gimple_assign_lhs (cmp);
+
+  // now goto next stmt to check if
+  gsi_next (&gsi);
+  if (gsi_end_p (gsi))
+    return false;
+
+  // to check if it is a simple assignment with nothing happening in rhs.
+  gassign *cast = dyn_cast <gassign *> (gsi_stmt (gsi));
+  if (!cast || gimple_assign_rhs_code (cast) != NOP_EXPR)
+    return false;
+
+  if (gimple_assign_rhs1 (cast) != booltmp)
+    return false;
+
+  // The true block in case of LE, contains NEGATE_EXPR
+  // i.e., -(a < b) or -(a != b)
+  if (code == LE_EXPR)
+  {
+    booltmp = gimple_assign_lhs (cast);
+    gsi_next (&gsi);
+    if (gsi_end_p (gsi))
+      return false;
+    cast = dyn_cast <gassign *> (gsi_stmt (gsi));
+    if (!cast || gimple_assign_rhs_code (cast) != NEGATE_EXPR)
+      return false;
+
+    if (gimple_assign_rhs1 (cast) != booltmp)
+      return false;
+  }
+
+  tree casttmp = gimple_assign_lhs (cast);
+
+  // Target bb must have single phi
+  if (!gimple_seq_empty_p (bb2->il.gimple.phi_nodes))
+    {
+      gphi *phi = NULL;
+      gphi_iterator saved_psi;
+      // Let this bb have only one phi for now
+      for (gphi_iterator psi = gsi_start_phis (bb2);
+          !gsi_end_p (psi); gsi_next (&psi))
+       {
+         if (phi)
+           return false;
+         phi = psi.phi ();
+         saved_psi = psi;
+       }
+
+      if (!phi)
+       return false;
+
+      tree arg_from_bb0 =
+       gimple_phi_arg_def_from_edge (phi, e_false);
+
+      tree arg_from_bb1 =
+       gimple_phi_arg_def_from_edge (phi,
+                                     single_succ_edge (bb1));
+
+      // For GE, the false path is -1, while for LE it is +1
+      if ((code == GE_EXPR && !integer_minus_onep (arg_from_bb0))
+         || (code == LE_EXPR && !integer_onep (arg_from_bb0)))
+       return false;
+
+      if (arg_from_bb1 != casttmp)
+       return false;
+
+      /* Build IFN_SPACESHIP (a, b, 1) */
+      tree arg3 = build_int_cst (integer_type_node, 1);
+
+      gcall *gc =
+       gimple_build_call_internal (IFN_SPACESHIP, 3,
+                                   arg1, arg2, arg3);
+
+      tree lhs = make_ssa_name (integer_type_node);
+      gimple_call_set_lhs (gc, lhs);
+
+      gimple_stmt_iterator gsi0 = gsi_for_stmt (stmt);
+      gsi_insert_before (&gsi0, gc, GSI_SAME_STMT);
+
+      /* Replace PHI */
+      tree phi_res = gimple_phi_result (phi);
+
+      if (!useless_type_conversion_p (TREE_TYPE (phi_res),
+                                     integer_type_node))
+       {
+         tree tmp = make_ssa_name (TREE_TYPE (phi_res));
+         gassign *conv =
+           gimple_build_assign (tmp, NOP_EXPR, lhs);
+         gsi_insert_before (&gsi0, conv, GSI_SAME_STMT);
+         lhs = tmp;
+       }
+
+      replace_uses_by (phi_res, lhs);
+
+      return true;
+    }
+
+  return false;
+}
+
 /* If target has spaceship<MODE>3 expander, pattern recognize
    <bb 2> [local count: 1073741824]:
    if (a_2(D) == b_3(D))
@@ -6643,7 +6802,8 @@ math_opts_dom_walker::after_dom_children (basic_block bb)
       else if (gimple_code (stmt) == GIMPLE_COND)
        {
          match_single_bit_test (&gsi, stmt);
-         optimize_spaceship (as_a <gcond *> (stmt));
+    if (!optimize_spaceship_for_ge_le (as_a <gcond *> (stmt)))
+           optimize_spaceship (as_a <gcond *> (stmt));
        }
       gsi_next (&gsi);
     }
-- 
2.51.0

Reply via email to