Hi,
Pinging this patch. Actually the need for this enhancement was observed
from PR106536 where,
int compare2(unsigned long long a, unsigned long long b)
{
return (a > b ? 1 : (a < b ? -1 : 0));
}
this was not being recognized as spaceship but on the other hand
int compare2(unsigned long long a, unsigned long long b)
{
return (a == b ? 0 : (a < b ? -1 : 1));
}
this one was. And I think identifying it as spaceship generates better
code than if not.
Thanks and regards,
Avinash Jayakar
> Hi,
>
> As suggested in the first version I have used the match pattern to
> recognize
> the spaceship pattern.
>
> cc'ed the maintainers of x86, aarch64 and s390 port to validate
> predicate added
> in target-supports.exp.
>
> Bootstrapped and regtested on x86_64-linux-gnu and powerpc64le-linux-
> gnu with
> no regressions. Please review.
>
> Changes from v1:
> * Added tests for all possible spaceship operator variants in c for
> signed/unsigned int.
> * Added a new effective target predicate for in target-supports.exp.
> Since I saw
> aarch64, x86 and s390 implement this operator, I have added those in
> the
> targets.
> * Use match.pd to detect the cfg more robustly.
>
> 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
> 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-24 Avinash Jayakar <[email protected]>
>
> gcc/ChangeLog:
> * match.pd: New match patterns to recognize spaceship
> variants.
> * tree-ssa-math-opts.cc (gimple_spaceship): Match function
> declaration.
> (match_spaceship): New function to recognize spaceship given
> phi node.
> (math_opts_dom_walker::after_dom_children): Add
> match_spaceship check.
>
> gcc/testsuite/ChangeLog:
> * lib/target-supports.exp (check_effective_target_spaceship):
> Add new
> proc for spaceship optab. x86, aarch64 and s390 included.
> * gcc.dg/spaceship_int_variants.c: New test.
> * gcc.dg/spaceship_uint_variants.c: New test.
> ---
> gcc/match.pd | 8 ++
> gcc/testsuite/gcc.dg/spaceship_int_variants.c | 102
> ++++++++++++++++++
> .../gcc.dg/spaceship_uint_variants.c | 102
> ++++++++++++++++++
> gcc/testsuite/lib/target-supports.exp | 8 ++
> gcc/tree-ssa-math-opts.cc | 33 +++++-
> 5 files changed, 251 insertions(+), 2 deletions(-)
> create mode 100644 gcc/testsuite/gcc.dg/spaceship_int_variants.c
> create mode 100644 gcc/testsuite/gcc.dg/spaceship_uint_variants.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 7f16fd4e081..0bcb5c0fe77 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -3661,6 +3661,14 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> @2)
> (if (wi::eq_p (wi::to_wide (@1), wi::to_wide (@3))))))
>
> +(if (INTEGRAL_TYPE_P (type))
> + (for op (lt ne)
> + (match (spaceship @0 @1)
> + (cond^ (le @0 @1) (negate (convert? (op @0 @1))) integer_onep)))
> + (for op (gt ne)
> + (match (spaceship @0 @1)
> + (cond^ (ge @0 @1) (convert? (op @0 @1)) integer_minus_onep))))
> +
> /* Saturation sub for signed integer. */
> (if (INTEGRAL_TYPE_P (type) && !TYPE_UNSIGNED (type))
> (match (signed_integer_sat_sub @0 @1)
> diff --git a/gcc/testsuite/gcc.dg/spaceship_int_variants.c
> b/gcc/testsuite/gcc.dg/spaceship_int_variants.c
> new file mode 100644
> index 00000000000..bf2fee63c17
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/spaceship_int_variants.c
> @@ -0,0 +1,102 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target spaceship } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-final { scan-tree-dump-times {\.SPACESHIP \([^,]+, [^,]+, -
> 1\)} 24 optimized } } */
> +
> +int sp1 (int a, int b)
> +{
> + return a < b ? -1 : a > b ? 1 : 0;
> +}
> +int sp1_1 (int a, int b)
> +{
> + return a < b ? -1 : a == b ? 0 : 1;
> +}
> +int sp1_2 (int a, int b)
> +{
> + return a < b ? -1 : a != b ? 1 : 0;
> +}
> +int sp1_3 (int a, int b)
> +{
> + return a < b ? -1 : a <= b ? 0 : 1;
> +}
> +int sp2 (int a, int b)
> +{
> + return a == b ? 0 : a > b ? 1 : -1;
> +}
> +int sp2_1 (int a, int b)
> +{
> + return a == b ? 0 : a < b ? -1 : 1;
> +}
> +int sp2_2 (int a, int b)
> +{
> + return a == b ? 0 : a >= b ? 1 : -1;
> +}
> +int sp2_3 (int a, int b)
> +{
> + return a == b ? 0 : a <= b ? -1 : 1;
> +}
> +int sp3 (int a, int b)
> +{
> + return a != b ? (a > b ? 1 : -1) : 0;
> +}
> +int sp3_1 (int a, int b)
> +{
> + return a != b ? (a < b ? -1 : 1) : 0;
> +}
> +int sp3_2 (int a, int b)
> +{
> + return a != b ? (a >= b ? 1 : -1) : 0;
> +}
> +int sp3_3 (int a, int b)
> +{
> + return a != b ? (a <= b ? -1 : 1) : 0;
> +}
> +int sp4 (int a, int b)
> +{
> + return a > b ? 1 : a == b ? 0 : -1;
> +}
> +int sp4_1 (int a, int b)
> +{
> + return a > b ? 1 : a < b ? -1 : 0;
> +}
> +int sp4_2 (int a, int b)
> +{
> + return a > b ? 1 : a != b ? -1 : 0;
> +}
> +int sp4_3 (int a, int b)
> +{
> + return a > b ? 1 : a >= b ? 0 : -1;
> +}
> +int sp5 (int a, int b)
> +{
> + return a <= b ? (a == b ? 0 : -1) : 1;
> +}
> +int sp5_1 (int a, int b)
> +{
> + return a <= b ? (a >= b ? 0 : -1) : 1;
> +}
> +int sp5_2 (int a, int b)
> +{
> + return a <= b ? (a != b ? -1 : 0) : 1;
> +}
> +int sp5_3 (int a, int b)
> +{
> + return a <= b ? (a < b ? -1 : 0) : 1;
> +}
> +int sp6 (int a, int b)
> +{
> + return a >= b ? (a == b ? 0 : 1) : -1;
> +}
> +int sp6_1 (int a, int b)
> +{
> + return a >= b ? (a != b ? 1 : 0) : -1;
> +}
> +int sp6_2 (int a, int b)
> +{
> + return a >= b ? (a <= b ? 0 : 1) : -1;
> +}
> +int sp6_3 (int a, int b)
> +{
> + return a >= b ? (a > b ? 1 : 0) : -1;
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/spaceship_uint_variants.c
> b/gcc/testsuite/gcc.dg/spaceship_uint_variants.c
> new file mode 100644
> index 00000000000..ee18e94185e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/spaceship_uint_variants.c
> @@ -0,0 +1,102 @@
> +/* { dg-do compile } */
> +/* { dg-require-effective-target spaceship } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +/* { dg-final { scan-tree-dump-times {\.SPACESHIP \([^,]+, [^,]+,
> 1\)} 24 optimized } } */
> +
> +int sp1 (unsigned int a, unsigned int b)
> +{
> + return a < b ? -1 : a > b ? 1 : 0;
> +}
> +int sp1_1 (unsigned int a, unsigned int b)
> +{
> + return a < b ? -1 : a == b ? 0 : 1;
> +}
> +int sp1_2 (unsigned int a, unsigned int b)
> +{
> + return a < b ? -1 : a != b ? 1 : 0;
> +}
> +int sp1_3 (unsigned int a, unsigned int b)
> +{
> + return a < b ? -1 : a <= b ? 0 : 1;
> +}
> +int sp2 (unsigned int a, unsigned int b)
> +{
> + return a == b ? 0 : a > b ? 1 : -1;
> +}
> +int sp2_1 (unsigned int a, unsigned int b)
> +{
> + return a == b ? 0 : a < b ? -1 : 1;
> +}
> +int sp2_2 (unsigned int a, unsigned int b)
> +{
> + return a == b ? 0 : a >= b ? 1 : -1;
> +}
> +int sp2_3 (unsigned int a, unsigned int b)
> +{
> + return a == b ? 0 : a <= b ? -1 : 1;
> +}
> +int sp3 (unsigned int a, unsigned int b)
> +{
> + return a != b ? (a > b ? 1 : -1) : 0;
> +}
> +int sp3_1 (unsigned int a, unsigned int b)
> +{
> + return a != b ? (a < b ? -1 : 1) : 0;
> +}
> +int sp3_2 (unsigned int a, unsigned int b)
> +{
> + return a != b ? (a >= b ? 1 : -1) : 0;
> +}
> +int sp3_3 (unsigned int a, unsigned int b)
> +{
> + return a != b ? (a <= b ? -1 : 1) : 0;
> +}
> +int sp4 (unsigned int a, unsigned int b)
> +{
> + return a > b ? 1 : a == b ? 0 : -1;
> +}
> +int sp4_1 (unsigned int a, unsigned int b)
> +{
> + return a > b ? 1 : a < b ? -1 : 0;
> +}
> +int sp4_2 (unsigned int a, unsigned int b)
> +{
> + return a > b ? 1 : a != b ? -1 : 0;
> +}
> +int sp4_3 (unsigned int a, unsigned int b)
> +{
> + return a > b ? 1 : a >= b ? 0 : -1;
> +}
> +int sp5 (unsigned int a, unsigned int b)
> +{
> + return a <= b ? (a == b ? 0 : -1) : 1;
> +}
> +int sp5_1 (unsigned int a, unsigned int b)
> +{
> + return a <= b ? (a >= b ? 0 : -1) : 1;
> +}
> +int sp5_2 (unsigned int a, unsigned int b)
> +{
> + return a <= b ? (a != b ? -1 : 0) : 1;
> +}
> +int sp5_3 (unsigned int a, unsigned int b)
> +{
> + return a <= b ? (a < b ? -1 : 0) : 1;
> +}
> +int sp6 (unsigned int a, unsigned int b)
> +{
> + return a >= b ? (a == b ? 0 : 1) : -1;
> +}
> +int sp6_1 (unsigned int a, unsigned int b)
> +{
> + return a >= b ? (a != b ? 1 : 0) : -1;
> +}
> +int sp6_2 (unsigned int a, unsigned int b)
> +{
> + return a >= b ? (a <= b ? 0 : 1) : -1;
> +}
> +int sp6_3 (unsigned int a, unsigned int b)
> +{
> + return a >= b ? (a > b ? 1 : 0) : -1;
> +}
> +
> diff --git a/gcc/testsuite/lib/target-supports.exp
> b/gcc/testsuite/lib/target-supports.exp
> index 855bdbcf55a..5fc3a0e59b7 100644
> --- a/gcc/testsuite/lib/target-supports.exp
> +++ b/gcc/testsuite/lib/target-supports.exp
> @@ -10403,6 +10403,14 @@ proc
> check_effective_target_sync_long_long_runtime { } {
> }
> }
>
> +# Return 1 if target supports spaceship optab
> +
> +proc check_effective_target_spaceship { } {
> + return [expr { [check_effective_target_x86]
> + || [istarget aarch64*-*-*]
> + || [istarget s390*-*-*] }]
> +}
> +
> # Return 1 if the target supports byte swap instructions.
>
> proc check_effective_target_bswap { } {
> diff --git a/gcc/tree-ssa-math-opts.cc b/gcc/tree-ssa-math-opts.cc
> index bb67dca560b..e9fc55708dd 100644
> --- a/gcc/tree-ssa-math-opts.cc
> +++ b/gcc/tree-ssa-math-opts.cc
> @@ -4121,6 +4121,7 @@ extern bool gimple_unsigned_integer_sat_add
> (tree, tree*, tree (*)(tree));
> extern bool gimple_unsigned_integer_sat_sub (tree, tree*, tree
> (*)(tree));
> extern bool gimple_unsigned_integer_sat_trunc (tree, tree*, tree
> (*)(tree));
> extern bool gimple_unsigned_integer_sat_mul (tree, tree*, tree
> (*)(tree));
> +extern bool gimple_spaceship (tree, tree*, tree (*)(tree));
>
> extern bool gimple_signed_integer_sat_add (tree, tree*, tree
> (*)(tree));
> extern bool gimple_signed_integer_sat_sub (tree, tree*, tree
> (*)(tree));
> @@ -4330,6 +4331,34 @@ match_saturation_mul (gimple_stmt_iterator
> *gsi, gphi *phi)
> ops[1]);
> }
>
> +static bool
> +match_spaceship (gimple_stmt_iterator *gsi, gphi *phi)
> +{
> + if (gimple_phi_num_args (phi) != 2)
> + return false;
> + tree ops[2];
> + tree phi_result = gimple_phi_result (phi);
> +
> + if (!gimple_spaceship (phi_result, ops, NULL))
> + return false;
> +
> + if (TYPE_MODE (TREE_TYPE (phi_result)) != TYPE_MODE (TREE_TYPE
> (ops[0])))
> + return false;
> +
> + if (optab_handler (spaceship_optab,
> + TYPE_MODE (TREE_TYPE (phi_result))) ==
> CODE_FOR_nothing)
> + return false;
> +
> + gcall *call = gimple_build_call_internal (IFN_SPACESHIP, 3,
> ops[0], ops[1],
> + build_int_cst
> + (integer_type_node,
> + TYPE_UNSIGNED
> (TREE_TYPE (ops[0]))
> + ? 1 : -1));
> + gimple_call_set_lhs (call, phi_result);
> + gsi_insert_before (gsi, call, GSI_SAME_STMT);
> + return true;
> +}
> +
> /*
> * Try to match saturation unsigned sub.
> * <bb 2> [local count: 1073741824]:
> @@ -6508,11 +6537,11 @@ math_opts_dom_walker::after_dom_children
> (basic_block bb)
>
> gimple_stmt_iterator gsi = gsi_after_labels (bb);
> gphi *phi = psi.phi ();
> -
> if (match_saturation_add (&gsi, phi)
> || match_saturation_sub (&gsi, phi)
> || match_saturation_trunc (&gsi, phi)
> - || match_saturation_mul (&gsi, phi))
> + || match_saturation_mul (&gsi, phi)
> + || match_spaceship (&gsi, phi))
> remove_phi_node (&psi, /* release_lhs_p */ false);
> }
>