}
}
+/* Expand the spaceship optab for floating-point operands.
+
+ If the result is compared against (-1, 0, 1 , 2), expand into
+ fcmpe + conditional branch insns.
+
+ Otherwise (the result is just stored as an integer), expand into
+ fcmpe + a sequence of conditional select/increment/invert insns. */
+void
+aarch64_expand_fp_spaceship (rtx dest, rtx op0, rtx op1, rtx hint)
+{
+ rtx cc_reg = gen_rtx_REG (CCFPEmode, CC_REGNUM);
+ emit_set_insn (cc_reg, gen_rtx_COMPARE (CCFPEmode, op0, op1));
+
+ rtx cc_gt = gen_rtx_GT (VOIDmode, cc_reg, const0_rtx);
+ rtx cc_lt = gen_rtx_LT (VOIDmode, cc_reg, const0_rtx);
+ rtx cc_un = gen_rtx_UNORDERED (VOIDmode, cc_reg, const0_rtx);
+
+ if (hint == const0_rtx)
+ {
+ rtx un_label = gen_label_rtx ();
+ rtx lt_label = gen_label_rtx ();
+ rtx gt_label = gen_label_rtx ();
+ rtx end_label = gen_label_rtx ();
+
+ rtx temp = gen_rtx_IF_THEN_ELSE (VOIDmode, cc_un,
+ gen_rtx_LABEL_REF (Pmode, un_label), pc_rtx);
+ aarch64_emit_unlikely_jump (gen_rtx_SET (pc_rtx, temp));
+
+ temp = gen_rtx_IF_THEN_ELSE (VOIDmode, cc_lt,
+ gen_rtx_LABEL_REF (Pmode, lt_label), pc_rtx);
+ emit_jump_insn (gen_rtx_SET (pc_rtx, temp));
+
+ temp = gen_rtx_IF_THEN_ELSE (VOIDmode, cc_gt,
+ gen_rtx_LABEL_REF (Pmode, gt_label), pc_rtx);
+ emit_jump_insn (gen_rtx_SET (pc_rtx, temp));
+
+ /* Equality. */
+ emit_move_insn (dest, const0_rtx);
+ emit_jump (end_label);
+
+ emit_label (un_label);
+ emit_move_insn (dest, const2_rtx);
+ emit_jump (end_label);
+
+ emit_label (gt_label);
+ emit_move_insn (dest, const1_rtx);
+ emit_jump (end_label);
+
+ emit_label (lt_label);
+ emit_move_insn (dest, constm1_rtx);
+
+ emit_label (end_label);
+ }
+ else
+ {
+ rtx temp0 = gen_reg_rtx (SImode);
+ rtx temp1 = gen_reg_rtx (SImode);
+ rtx cc_ungt = gen_rtx_UNGT (VOIDmode, cc_reg, const0_rtx);
+
+ /* The value of hint is stored if the operands are unordered. */
+ rtx temp_un = gen_int_mode (UINTVAL (hint) - 1, SImode);
+ if (!aarch64_reg_zero_or_m1_or_1 (temp_un, SImode))
+ temp_un = force_reg (SImode, temp_un);
+
+ emit_set_insn (temp0, gen_rtx_IF_THEN_ELSE (SImode, cc_lt,
+ constm1_rtx, const0_rtx));
+ emit_set_insn (temp1, gen_rtx_IF_THEN_ELSE (SImode, cc_un,
+ temp_un, const0_rtx));
+ emit_set_insn (dest, gen_rtx_IF_THEN_ELSE (SImode, cc_ungt,
+ gen_rtx_PLUS (SImode, temp1, const1_rtx), temp0));
+ }
+}
+
/* Target-specific selftests. */
#if CHECKING_P
[(set_attr "type" "alus_ext")]
)
+;; <=> operator pattern (integer)
+;; (a == b) ? 0 : (a < b) ? -1 : 1.
+(define_expand "spaceship<mode>4"
+ [(match_operand:SI 0 "register_operand")
+ (match_operand:GPI 1 "register_operand")
+ (match_operand:GPI 2 "register_operand")
+ (match_operand:SI 3 "const_int_operand")]
+ ""
+ {
+ // 1 indicates unsigned comparison, -1 indicates signed.
+ gcc_assert (operands[3] == constm1_rtx || operands[3] == const1_rtx);
+
+ rtx cc_reg = aarch64_gen_compare_reg (EQ, operands[1], operands[2]);
+ RTX_CODE code_gt = operands[3] == const1_rtx ? GTU : GT;
+ RTX_CODE code_lt = operands[3] == const1_rtx ? LTU : LT;
+
+ rtx cc_gt = gen_rtx_fmt_ee (code_gt, VOIDmode, cc_reg, const0_rtx);
+ rtx cc_lt = gen_rtx_fmt_ee (code_lt, VOIDmode, cc_reg, const0_rtx);
+
+ rtx temp = gen_reg_rtx (SImode);
+ emit_insn (gen_rtx_SET (temp, gen_rtx_IF_THEN_ELSE (SImode, cc_gt,
+ const1_rtx, const0_rtx)));
+ emit_insn (gen_rtx_SET (operands[0], gen_rtx_IF_THEN_ELSE (SImode, cc_lt,
+ constm1_rtx, temp)));
+ DONE;
+ }
+)
+
+;; <=> operator pattern (floating-point)
+;; (a == b) ? 0 : (a < b) ? -1 : (a > b) ? 1 : UNORDERED.
+(define_expand "spaceship<mode>4"
+ [(match_operand:SI 0 "register_operand")
+ (match_operand:GPF 1 "register_operand")
+ (match_operand:GPF 2 "register_operand")
+ (match_operand:SI 3 "const_int_operand")]
+ "TARGET_FLOAT"
+ {
+ aarch64_expand_fp_spaceship (operands[0], operands[1], operands[2],
+ operands[3]);
+ DONE;
+ }
+)
+
;; -------------------------------------------------------------------
;; Store-flag and conditional select insns
;; -------------------------------------------------------------------
--- /dev/null
+// PR117013
+/* { dg-do run } */
+/* { dg-options "-O2 -std=c++20 -save-temps -fno-schedule-insns2" } */
+/* { dg-final { check-function-bodies "**" "" ""} } */
+
+#include <compare>
+#include <stdint.h>
+
+/* Some implementation-defined value (other than 2) to represent
+ partial_ordering::unordered (that for libc++ in this case). */
+#define IMP_UN -127
+
+#define SPACESHIP_FN(TYPE) \
+ [[gnu::noipa]] \
+ auto ss_##TYPE (TYPE a, TYPE b) \
+ { return a <=> b; } \
+
+#define SPACESHIP_FN_NN(TYPE) \
+ [[gnu::noipa, gnu::optimize ("-ffinite-math-only")]] \
+ auto ss_##TYPE##_no_nans (TYPE a, TYPE b) \
+ { return a <=> b; } \
+
+/* <=> implementation for floating-point operands. */
+#define SPACESHIP_FP_IDIOM(TYPE) \
+ [[gnu::noipa]] \
+ int ss_##TYPE##_idiom (TYPE a, TYPE b) \
+ { return ((a) == (b) ? 0 : (a) < (b) ? -1 : (a) > (b) ? 1 : IMP_UN); } \
+
+#define RUN_TEST(TYPE, ARGA, ARGB, EXPECT, SUFF) \
+ if (ss_##TYPE##SUFF ((ARGA), (ARGB)) != (EXPECT)) \
+ __builtin_abort(); \
+
+/*
+** _Z8ss_floatff:
+** fcmpe s0, s1
+** csinv (w[0-9]+), wzr, wzr, pl
+** cset (w[0-9]+), vs
+** csinc w0, \1, \2, ls
+** ret
+*/
+SPACESHIP_FN(float);
+
+/*
+** _Z16ss_float_no_nansff:
+** fcmpe s0, s1
+** csinv (w[0-9]+), wzr, wzr, pl
+** csinc w0, \1, wzr, ls
+** ret
+*/
+SPACESHIP_FN_NN(float);
+
+/*
+** _Z9ss_doubledd:
+** fcmpe d0, d1
+** csinv (w[0-9]+), wzr, wzr, pl
+** cset (w[0-9]+), vs
+** csinc w0, \1, \2, ls
+** ret
+*/
+SPACESHIP_FN(double);
+
+/*
+** _Z17ss_double_no_nansdd:
+** fcmpe d0, d1
+** csinv (w[0-9]+), wzr, wzr, pl
+** csinc w0, \1, wzr, ls
+** ret
+*/
+SPACESHIP_FN_NN(double);
+
+/*
+** _Z14ss_float_idiomff:
+** fcmpe s0, s1
+** csinv (w[0-9]+), wzr, wzr, pl
+** mov (w[0-9]+), -128
+** csel (w[0-9]+), \2, wzr, vs
+** csinc w0, \1, \3, ls
+** ret
+*/
+SPACESHIP_FP_IDIOM(float);
+
+/*
+** _Z15ss_double_idiomdd:
+** fcmpe d0, d1
+** csinv (w[0-9]+), wzr, wzr, pl
+** mov (w[0-9]+), -128
+** csel (w[0-9]+), \2, wzr, vs
+** csinc w0, \1, \3, ls
+** ret
+*/
+SPACESHIP_FP_IDIOM(double);
+
+/*
+** _Z10ss_int32_tii:
+** cmp w0, w1
+** cset (w[0-9]+), gt
+** csinv w0, \1, wzr, ge
+** ret
+*/
+SPACESHIP_FN(int32_t);
+
+/*
+** _Z10ss_int64_tll:
+** cmp x0, x1
+** cset (w[0-9]+), gt
+** csinv w0, \1, wzr, ge
+** ret
+*/
+SPACESHIP_FN(int64_t);
+
+/*
+** _Z11ss_uint32_tjj:
+** cmp w0, w1
+** cset (w[0-9]+), hi
+** csinv w0, \1, wzr, cs
+** ret
+*/
+SPACESHIP_FN(uint32_t);
+
+/*
+** _Z11ss_uint64_tmm:
+** cmp x0, x1
+** cset (w[0-9]+), hi
+** csinv w0, \1, wzr, cs
+** ret
+*/
+SPACESHIP_FN(uint64_t);
+
+
+int
+main()
+{
+ /* Single precision floating point. */
+ RUN_TEST (float, -1.0f, 1.0f, std::partial_ordering::less,);
+ RUN_TEST (float, -1.0f, 1.0f, -1, _idiom);
+
+ RUN_TEST (float, 1.0f, -1.0f, std::partial_ordering::greater,);
+ RUN_TEST (float, 1.0f, -1.0f, 1, _idiom);
+
+ RUN_TEST (float, -1.0f, -1.0f, std::partial_ordering::equivalent,);
+ RUN_TEST (float, -1.0f, -1.0f, 0, _idiom);
+
+ RUN_TEST (float, __builtin_nanf(""), 1.0f, std::partial_ordering::unordered,);
+ RUN_TEST (float, __builtin_nanf(""), 1.0f, IMP_UN, _idiom);
+ RUN_TEST (float, 1.0f ,__builtin_nanf(""), std::partial_ordering::unordered,);
+ RUN_TEST (float, 1.0f, __builtin_nanf(""), IMP_UN, _idiom);
+
+ /* No-NaNs. */
+ RUN_TEST (float, -1.0f, 1.0f, std::partial_ordering::less, _no_nans);
+ RUN_TEST (float, 1.0f, -1.0f, std::partial_ordering::greater, _no_nans);
+ RUN_TEST (float, -1.0f, -1.0f, std::partial_ordering::equivalent, _no_nans);
+
+ /* Double precision floating point. */
+ RUN_TEST (double, -1.0f, 1.0f, std::partial_ordering::less,);
+ RUN_TEST (double, -1.0f, 1.0f, -1, _idiom);
+
+ RUN_TEST (double, 1.0f, -1.0f, std::partial_ordering::greater,);
+ RUN_TEST (double, 1.0f, -1.0f, 1, _idiom);
+
+ RUN_TEST (double, -1.0f, -1.0f, std::partial_ordering::equivalent,);
+ RUN_TEST (double, -1.0f, -1.0f, 0, _idiom);
+
+ RUN_TEST (double, __builtin_nanf(""), 1.0f, std::partial_ordering::unordered,);
+ RUN_TEST (double, __builtin_nanf(""), 1.0f, IMP_UN, _idiom);
+ RUN_TEST (double, 1.0f, __builtin_nanf(""), std::partial_ordering::unordered,);
+ RUN_TEST (double, 1.0f, __builtin_nanf(""), IMP_UN, _idiom);
+
+ /* No-NaNs. */
+ RUN_TEST (double, -1.0f, 1.0f, std::partial_ordering::less, _no_nans);
+ RUN_TEST (double, 1.0f, -1.0f, std::partial_ordering::greater, _no_nans);
+ RUN_TEST (double, -1.0f, -1.0f, std::partial_ordering::equivalent, _no_nans);
+
+ /* Single integer. */
+ RUN_TEST (int32_t, -42, 0, std::strong_ordering::less,);
+ RUN_TEST (int32_t, 0, -42, std::strong_ordering::greater,);
+ RUN_TEST (int32_t, 42, 42, std::strong_ordering::equal,);
+
+ RUN_TEST (uint32_t, 0, 42, std::strong_ordering::less,);
+ RUN_TEST (uint32_t, 42, 0, std::strong_ordering::greater,);
+ RUN_TEST (uint32_t, 42, 42, std::strong_ordering::equal,);
+
+ /* Double integer. */
+ RUN_TEST (int64_t, -42, 0, std::strong_ordering::less,);
+ RUN_TEST (int64_t, 42, 0, std::strong_ordering::greater,);
+ RUN_TEST (int64_t, 42, 42, std::strong_ordering::equal,);
+
+ RUN_TEST (uint64_t, 0, 42, std::strong_ordering::less,);
+ RUN_TEST (uint64_t, 42, 0, std::strong_ordering::greater,);
+ RUN_TEST (uint64_t, 42, 42, std::strong_ordering::equal,);
+
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+// PR117013
+/* { dg-do run } */
+// { dg-options "-O2 -std=c++20 -save-temps" }
+
+#include <compare>
+
+#ifndef fp_type
+#define fp_type float
+#endif
+
+#define TEST_SS_IDIOM(ARGA, ARGB, EXPECT) \
+ if (spaceship_idiom ((ARGA), (ARGB)) != (EXPECT)) \
+ __builtin_abort(); \
+
+#define TEST_BR_ON_SS(ARGA, ARGB, EXPECT) \
+ if(branch_on_spaceship ((ARGA), (ARGB)) != (EXPECT)) \
+ __builtin_abort(); \
+
+
+#define RUN_TEST(ARGA, ARGB, EXPECT) \
+ TEST_SS_IDIOM(ARGA, ARGB, EXPECT) \
+ TEST_BR_ON_SS(ARGA, ARGB, EXPECT) \
+
+/* Test when .SPACESHIP prompts the back end to implement <=> with
+ conditional branches (only applies to floating-point operands). */
+
+[[gnu::noipa]] auto
+equiv() { return std::partial_ordering::equivalent; }
+[[gnu::noipa]] auto
+less() { return std::partial_ordering::less; }
+[[gnu::noipa]] auto
+greater() { return std::partial_ordering::greater; }
+[[gnu::noipa]] auto
+unordered() { return std::partial_ordering::unordered; }
+
+auto
+spaceship_idiom(fp_type a, fp_type b)
+{
+ if (a == b)
+ return equiv();
+ if (a < b)
+ return less();
+ if (a > b)
+ return greater();
+ return unordered();
+}
+
+auto
+branch_on_spaceship(fp_type a, fp_type b)
+{
+ auto res = a <=> b;
+ if (res == 0)
+ return equiv();
+ else if (res < 0)
+ return less();
+ else if (res > 0)
+ return greater();
+ return unordered();
+}
+
+int
+main()
+{
+ RUN_TEST (-1.0f, 1.0f, std::partial_ordering::less);
+ RUN_TEST (1.0f, -1.0f, std::partial_ordering::greater);
+ RUN_TEST (1.0f, 1.0f, std::partial_ordering::equivalent);
+ RUN_TEST (1.0f, __builtin_nanf(""), std::partial_ordering::unordered);
+ RUN_TEST (__builtin_nanf(""), 1.0f, std::partial_ordering::unordered);
+}
+
+/* { dg-final { scan-assembler-not "\tfcmp\t" } } */
+/* { dg-final { scan-assembler-times "\tfcmpe\t" 2 } } */
\ No newline at end of file