Extend cbranch, cstore and cmov patterns to accomodate TImode operands.
This leads to potentially much better code for 128-bit integer compares
and conditional moves by making use of the conditional instructions.
For example, (result = a < b ? c : d) where all operands are uint128
may now be implemented via cmp+sbcs for the compare and a pair of csel
for the move.
Andrea wrote most of the patch a while ago in the linked PR, I just
got cmov working and fixed an error that fell out of testing.
Bootstrapped and regtested on aarch64-linux-gnu. No known regressions.
gcc/
PR target/116509
* config/aarch64/aarch64-protos.h (aarch64_gen_compare_reg): Add new
proto with pointer for insn code.
* config/aarch64/aarch64.cc (aarch64_gen_compare_reg): New version
which accepts a pointer for the insn code and possibly changes it.
Extend handling for TImode. Existing version now wrapps this one.
* config/aarch64/aarch64.md (cbranch<GPI:mode>4): Change to...
(cbranch<mode>4): ... this. Handle TImode inputs as well.
(cstore<mode>4): Accept and handle TImode inputs.
("*cmovti_insn"): New.
* config/aarch64/iterators.md (GPI_TI): New mode iterator.
gcc/testsuite/
PR target/116509
* gcc.target/aarch64/pr116509-1.c: New test.
* gcc.target/aarch64/pr116509-2.c: New test.
* gcc.target/aarch64/pr116509-3.c: New test.
Co-authored-by: Andrea Pinski <[email protected]>
Signed-off-by: David Faust <[email protected]>
---
gcc/config/aarch64/aarch64-protos.h | 1 +
gcc/config/aarch64/aarch64.cc | 68 +++++++++--
gcc/config/aarch64/aarch64.md | 59 ++++++++--
gcc/config/aarch64/iterators.md | 3 +
gcc/testsuite/gcc.target/aarch64/pr116509-1.c | 55 +++++++++
gcc/testsuite/gcc.target/aarch64/pr116509-2.c | 75 ++++++++++++
gcc/testsuite/gcc.target/aarch64/pr116509-3.c | 110 ++++++++++++++++++
7 files changed, 352 insertions(+), 19 deletions(-)
create mode 100644 gcc/testsuite/gcc.target/aarch64/pr116509-1.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/pr116509-2.c
create mode 100644 gcc/testsuite/gcc.target/aarch64/pr116509-3.c
diff --git a/gcc/config/aarch64/aarch64-protos.h
b/gcc/config/aarch64/aarch64-protos.h
index e8ae3d42794..d21bcbcf80f 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -1121,6 +1121,7 @@ void aarch64_gen_unlikely_cbranch (enum rtx_code,
machine_mode cc_mode,
bool aarch64_legitimate_address_p (machine_mode, rtx, bool,
aarch64_addr_query_type = ADDR_QUERY_M);
machine_mode aarch64_select_cc_mode (RTX_CODE, rtx, rtx);
+rtx aarch64_gen_compare_reg (RTX_CODE*, rtx, rtx);
rtx aarch64_gen_compare_reg (RTX_CODE, rtx, rtx);
rtx aarch64_gen_compare_split_imm24 (rtx, rtx, rtx);
bool aarch64_maxmin_plus_const (rtx_code, rtx *, bool);
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index f9d906f449f..80e218da5d8 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -3147,7 +3147,7 @@ emit_set_insn (rtx x, rtx y)
/* X and Y are two things to compare using CODE. Emit the compare insn and
return the rtx for register 0 in the proper mode. */
rtx
-aarch64_gen_compare_reg (RTX_CODE code, rtx x, rtx y)
+aarch64_gen_compare_reg (RTX_CODE *code, rtx x, rtx y)
{
machine_mode cmp_mode = GET_MODE (x);
machine_mode cc_mode;
@@ -3155,30 +3155,76 @@ aarch64_gen_compare_reg (RTX_CODE code, rtx x, rtx y)
if (cmp_mode == TImode)
{
- gcc_assert (code == NE);
+ switch (*code)
+ {
+ case GTU:
+ std::swap (x, y);
+ *code = LTU;
+ break;
+ case LEU:
+ std::swap (x, y);
+ *code = GEU;
+ break;
+ case GT:
+ std::swap (x, y);
+ *code = LT;
+ break;
+ case LE:
+ std::swap (x, y);
+ *code = GE;
+ break;
+ default:
+ ;
+ }
+ rtx x_lo = operand_subword_force (x, 0, TImode);
+ rtx y_lo = operand_subword_force (y, 0, TImode);
+ rtx x_hi = operand_subword_force (x, 1, TImode);
+ rtx y_hi = operand_subword_force (y, 1, TImode);
+ x_lo = force_reg (DImode, x_lo);
+ if (!aarch64_plus_operand (y_lo, DImode))
+ y_lo = force_reg (DImode, y_lo);
cc_mode = CCmode;
cc_reg = gen_rtx_REG (cc_mode, CC_REGNUM);
- rtx x_lo = operand_subword (x, 0, 0, TImode);
- rtx y_lo = operand_subword (y, 0, 0, TImode);
emit_set_insn (cc_reg, gen_rtx_COMPARE (cc_mode, x_lo, y_lo));
-
- rtx x_hi = operand_subword (x, 1, 0, TImode);
- rtx y_hi = operand_subword (y, 1, 0, TImode);
- emit_insn (gen_ccmpccdi (cc_reg, cc_reg, x_hi, y_hi,
- gen_rtx_EQ (cc_mode, cc_reg, const0_rtx),
- GEN_INT (AARCH64_EQ)));
+ if (*code == NE || *code == EQ)
+ emit_insn (gen_ccmpccdi (cc_reg, cc_reg, x_hi, y_hi,
+ gen_rtx_EQ (cc_mode, cc_reg, const0_rtx),
+ GEN_INT (AARCH64_EQ)));
+ else
+ {
+ /* FIXME: Remove this temp register, use xzr. */
+ rtx tmp = gen_reg_rtx (DImode);
+ x_hi = force_reg (DImode, x_hi);
+ y_hi = force_reg (DImode, y_hi);
+ if (unsigned_condition_p (*code))
+ emit_insn (gen_usubdi3_carryinC (tmp, x_hi, y_hi));
+ else
+ emit_insn (gen_subdi3_carryinV (tmp, x_hi, y_hi));
+ }
}
else
{
- cc_mode = SELECT_CC_MODE (code, x, y);
+ cc_mode = SELECT_CC_MODE (*code, x, y);
cc_reg = gen_rtx_REG (cc_mode, CC_REGNUM);
emit_set_insn (cc_reg, gen_rtx_COMPARE (cc_mode, x, y));
}
return cc_reg;
}
+/* X and Y are two things to compare using CODE. Emit the compare insn and
+ return the rtx for register 0 in the proper mode.
+ CODE cannot not change. */
+rtx
+aarch64_gen_compare_reg (RTX_CODE code, rtx x, rtx y)
+{
+ RTX_CODE old_code = code;
+ rtx res = aarch64_gen_compare_reg (&code, x, y);
+ gcc_checking_assert (code == old_code);
+ return res;
+}
+
/* Similarly, but maybe zero-extend Y if Y_MODE < SImode. */
static rtx
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index 302658c970d..1d1550543d8 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -818,23 +818,27 @@ (define_constants
;; 4) Otherwise, emit a CMP+B<cond> sequence.
;; -------------------------------------------------------------------
-(define_expand "cbranch<GPI:mode>4"
+(define_expand "cbranch<mode>4"
[(set (pc) (if_then_else (match_operator 0 "aarch64_comparison_operator"
- [(match_operand:GPI 1 "register_operand")
- (match_operand:GPI 2 "aarch64_plus_operand")])
+ [(match_operand:GPI_TI 1 "register_operand")
+ (match_operand:GPI_TI 2 "aarch64_plus_operand")])
(label_ref (match_operand 3))
(pc)))]
""
{
- if (TARGET_CMPBR && aarch64_cb_rhs (GET_CODE (operands[0]), operands[2]))
+ if (<MODE>mode != TImode
+ && TARGET_CMPBR
+ && aarch64_cb_rhs (GET_CODE (operands[0]), operands[2]))
{
/* The branch is supported natively. */
}
else
{
- operands[1] = aarch64_gen_compare_reg (GET_CODE (operands[0]),
+ rtx_code code = GET_CODE (operands[0]);
+ operands[1] = aarch64_gen_compare_reg (&code,
operands[1], operands[2]);
operands[2] = const0_rtx;
+ PUT_CODE (operands[0], code);
}
}
)
@@ -4780,13 +4784,15 @@ (define_expand "spaceship<mode>4"
(define_expand "cstore<mode>4"
[(set (match_operand:SI 0 "register_operand")
(match_operator:SI 1 "aarch64_comparison_operator"
- [(match_operand:GPI 2 "register_operand")
- (match_operand:GPI 3 "aarch64_plus_operand")]))]
+ [(match_operand:GPI_TI 2 "register_operand")
+ (match_operand:GPI_TI 3 "aarch64_plus_operand")]))]
""
"
- operands[2] = aarch64_gen_compare_reg (GET_CODE (operands[1]), operands[2],
+ rtx_code code = GET_CODE (operands[1]);
+ operands[2] = aarch64_gen_compare_reg (&code, operands[2],
operands[3]);
operands[3] = const0_rtx;
+ PUT_CODE (operands[1], code);
"
)
@@ -4896,6 +4902,43 @@ (define_insn "*cmov<mode>_insn"
}
)
+;; 128-bit version of above.
+(define_insn_and_split "*cmovti_insn"
+ [(set (match_operand:TI 0 "register_operand")
+ (if_then_else:TI
+ (match_operator 1 "aarch64_comparison_operator"
+ [(match_operand 2 "cc_register") (const_int 0)])
+ (match_operand:TI 3 "register_operand")
+ (match_operand:TI 4 "register_operand")))]
+ ""
+ "#"
+ "can_create_pseudo_p ()"
+ [(set (match_operand 5)
+ (if_then_else:DI (match_dup 1) (match_operand 6) (match_operand 7)))
+ (set (match_operand 8)
+ (if_then_else:DI (match_dup 1) (match_operand 9) (match_operand 10)))]
+ {
+ rtx dst = operands[0];
+ rtx a = operands[3];
+ rtx b = operands[4];
+ if (!REG_P (dst))
+ dst = gen_reg_rtx (TImode);
+ if (!REG_P (a))
+ a = force_reg (TImode, a);
+ if (!REG_P (b))
+ b = force_reg (TImode, b);
+
+ operands[5] = gen_lowpart (DImode, dst);
+ operands[6] = gen_lowpart (DImode, a);
+ operands[7] = gen_lowpart (DImode, b);
+ operands[8] = gen_highpart (DImode, dst);
+ operands[9] = gen_highpart (DImode, a);
+ operands[10] = gen_highpart (DImode, b);
+ }
+ [(set_attr "type" "csel")]
+)
+
+
;; zero_extend version of above
(define_insn "*cmovsi_insn_uxtw"
[(set (match_operand:DI 0 "register_operand")
diff --git a/gcc/config/aarch64/iterators.md b/gcc/config/aarch64/iterators.md
index 8ed91d021f0..e61201d6509 100644
--- a/gcc/config/aarch64/iterators.md
+++ b/gcc/config/aarch64/iterators.md
@@ -29,6 +29,9 @@ (define_mode_iterator CCFP_CCFPE [CCFP CCFPE])
;; Iterator for General Purpose Integer registers (32- and 64-bit modes)
(define_mode_iterator GPI [SI DI])
+;; Iterator for General Purpose Integer registers plus TI
+(define_mode_iterator GPI_TI [SI DI TI])
+
;; Iterator for HI, SI, DI, some instructions can only work on these modes.
(define_mode_iterator GPI_I16 [(HI "TARGET_FP_F16INST") SI DI])
diff --git a/gcc/testsuite/gcc.target/aarch64/pr116509-1.c
b/gcc/testsuite/gcc.target/aarch64/pr116509-1.c
new file mode 100644
index 00000000000..040c5f25745
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr116509-1.c
@@ -0,0 +1,55 @@
+/* { dg-do compile { target int128 } } */
+/* { dg-options { "-O2" } } */
+
+/* PR target/116509.
+ 128-bit int compares should be handled by cmp + ccmp/sbcs. */
+
+/* { dg-final { scan-assembler-not "b\\." } } */
+/* { dg-final { scan-assembler-times "\tcmp\t" 10 } } */
+/* { dg-final { scan-assembler-times "sbcs\t" 8 } } */
+/* { dg-final { scan-assembler-times "ccmp\t" 2 } } */
+/* { dg-final { scan-assembler-times "cset\tw0" 10 } } */
+
+int ltu(unsigned __int128 a, unsigned __int128 b)
+{
+ return a < b;
+}
+
+int gtu(unsigned __int128 a, unsigned __int128 b)
+{
+ return a > b;
+}
+int geu(unsigned __int128 a, unsigned __int128 b)
+{
+ return a >= b;
+}
+int leu(unsigned __int128 a, unsigned __int128 b)
+{
+ return a <= b;
+}
+int eq(unsigned __int128 a, unsigned __int128 b)
+{
+ return a == b;
+}
+int ne(unsigned __int128 a, unsigned __int128 b)
+{
+ return a != b;
+}
+int lt(__int128 a, __int128 b)
+{
+ return a < b;
+}
+int gt(__int128 a, __int128 b)
+{
+ return a > b;
+}
+int ge(__int128 a, __int128 b)
+{
+ return a >= b;
+}
+int le(__int128 a, __int128 b)
+{
+ return a <= b;
+}
+
+
diff --git a/gcc/testsuite/gcc.target/aarch64/pr116509-2.c
b/gcc/testsuite/gcc.target/aarch64/pr116509-2.c
new file mode 100644
index 00000000000..aa0dcdf8b07
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr116509-2.c
@@ -0,0 +1,75 @@
+/* { dg-do compile { target int128 } } */
+/* { dg-options { "-O2" } } */
+
+/* PR target/116509. */
+/* 128-bit conditional moves should be handled by a pair of csel
+ rather than by branching. */
+
+/* { dg-final { scan-assembler-not "b\\." } } */
+/* { dg-final { scan-assembler-times "csel\tx0" 10 } } */
+/* { dg-final { scan-assembler-times "csel\tx1" 10 } } */
+
+unsigned __int128
+ltu (unsigned __int128 a, unsigned __int128 b,
+ unsigned __int128 c, unsigned __int128 d)
+{
+ return a < b ? c : d;
+}
+
+unsigned __int128
+gtu (unsigned __int128 a, unsigned __int128 b,
+ unsigned __int128 c, unsigned __int128 d)
+{
+ return a > b ? c : d;
+}
+
+unsigned __int128
+geu (unsigned __int128 a, unsigned __int128 b,
+ unsigned __int128 c, unsigned __int128 d)
+{
+ return a >= b ? c : d;
+}
+
+unsigned __int128
+leu (unsigned __int128 a, unsigned __int128 b,
+ unsigned __int128 c, unsigned __int128 d)
+{
+ return a <= b ? c : d;
+}
+
+unsigned __int128
+eq (unsigned __int128 a, unsigned __int128 b,
+ unsigned __int128 c, unsigned __int128 d)
+{
+ return a == b ? c : d;
+}
+
+unsigned __int128
+ne (unsigned __int128 a, unsigned __int128 b,
+ unsigned __int128 c, unsigned __int128 d)
+{
+ return a != b ? c : d;
+}
+
+unsigned __int128
+lt (__int128 a, __int128 b, __int128 c, __int128 d)
+{
+ return a < b ? c : d;
+}
+unsigned __int128
+gt (__int128 a, __int128 b, __int128 c, __int128 d)
+{
+ return a > b ? c : d;
+}
+
+unsigned __int128
+ge(__int128 a, __int128 b, __int128 c, __int128 d)
+{
+ return a >= b ? c : d;
+}
+
+unsigned __int128
+le(__int128 a, __int128 b, __int128 c, __int128 d)
+{
+ return a <= b ? c : d;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/pr116509-3.c
b/gcc/testsuite/gcc.target/aarch64/pr116509-3.c
new file mode 100644
index 00000000000..ffbe239cdc5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/pr116509-3.c
@@ -0,0 +1,110 @@
+/* { dg-do compile { target int128 } } */
+/* { dg-options { "-O2" } } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+/* PR target/116509. */
+/* 128-bit conditional branches should be handled with cmp + sbcs/ccmp. */
+
+int f(void);
+int g(void);
+
+/*
+** ltu:
+** cmp x[02], x[02]
+** sbcs x[0-9]+, x[13], x[13]
+** ...
+*/
+int ltu(unsigned __int128 a, unsigned __int128 b)
+{
+ return a < b ? f () : g();
+}
+/*
+** gtu:
+** cmp x[02], x[02]
+** sbcs x[0-9]+, x[13], x[13]
+** ...
+*/
+int gtu(unsigned __int128 a, unsigned __int128 b)
+{
+ return a > b ? f () : g();
+}
+/*
+** geu:
+** cmp x[02], x[02]
+** sbcs x[0-9]+, x[13], x[13]
+** ...
+*/
+int geu(unsigned __int128 a, unsigned __int128 b)
+{
+ return a >= b ? f () : g();
+}
+/*
+** leu:
+** cmp x[02], x[02]
+** sbcs x[0-9]+, x[13], x[13]
+** ...
+*/
+int leu(unsigned __int128 a, unsigned __int128 b)
+{
+ return a <= b ? f () : g();
+}
+/*
+** eq:
+** cmp x[02], x[02]
+** ccmp x[13], x[13], 0, eq
+** ...
+*/
+int eq(unsigned __int128 a, unsigned __int128 b)
+{
+ return a == b ? f () : g();
+}
+/*
+** ne:
+** cmp x[02], x[02]
+** ccmp x[13], x[13], 0, eq
+** ...
+*/
+int ne(unsigned __int128 a, unsigned __int128 b)
+{
+ return a != b ? f () : g();
+}
+/*
+** lt:
+** cmp x[02], x[02]
+** sbcs x[0-9]+, x[13], x[13]
+** ...
+*/
+int lt(__int128 a, __int128 b)
+{
+ return a < b ? f () : g();
+}
+/*
+** gt:
+** cmp x[02], x[02]
+** sbcs x[0-9]+, x[13], x[13]
+** ...
+*/
+int gt(__int128 a, __int128 b)
+{
+ return a > b ? f () : g();
+}
+/*
+** ge:
+** cmp x[02], x[02]
+** sbcs x[0-9]+, x[13], x[13]
+** ...
+*/
+int ge(__int128 a, __int128 b)
+{
+ return a >= b ? f () : g();
+}
+/*
+** le:
+** cmp x[02], x[02]
+** sbcs x[0-9]+, x[13], x[13]
+** ...
+*/
+int le(__int128 a, __int128 b)
+{
+ return a <= b ? f () : g();
+}
--
2.53.0