This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[SH] PR 52933 - Use div0s insn for integer sign comparisons
- From: Oleg Endo <oleg dot endo at t-online dot de>
- To: gcc-patches <gcc-patches at gcc dot gnu dot org>
- Date: Mon, 13 Aug 2012 22:08:52 +0200
- Subject: [SH] PR 52933 - Use div0s insn for integer sign comparisons
Hello,
This patch adds basic support for utilizing the SH div0s instruction to
simplify some integer sign comparisons such as '(a < 0) == (b < 0)'.
Tested on rev 190332 with
make -k check RUNTESTFLAGS="--target_board=sh-sim
\{-m2/-ml,-m2/-mb,-m2a/-mb,-m4/-ml,-m4/-mb,-m4a/-ml,-m4a/-mb}"
and no new failures.
OK?
Cheers,
Oleg
ChangeLog:
PR target/52933
* config/sh/sh.md (cmp_div0s_0, cmp_div0s_1, *cmp_div0s_0,
*cmp_div0s_1, *cbranch_div0s, *movsicc_div0s): New insns.
* config/sh/sh.c (sh_rtx_costs): Handle div0s patterns.
testsuite/ChangeLog:
PR target/52933
* gcc.target/sh/pr52933-1.c: New.
* gcc.target/sh/pr52933-2.c: New.
Index: gcc/testsuite/gcc.target/sh/pr52933-1.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr52933-1.c (revision 0)
+++ gcc/testsuite/gcc.target/sh/pr52933-1.c (revision 0)
@@ -0,0 +1,168 @@
+/* Check that the div0s instruction is used for integer sign comparisons.
+ Each test case is expected to emit at least one div0s insn.
+ Problems when combining the div0s comparison result with surrounding
+ logic usually show up as redundant tst insns. */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */
+/* { dg-final { scan-assembler-times "div0s" 25 } } */
+/* { dg-final { scan-assembler-not "tst" } } */
+
+typedef unsigned char bool;
+
+int other_func_a (int, int);
+int other_func_b (int, int);
+
+bool
+test_00 (int a, int b)
+{
+ return (a ^ b) >= 0;
+}
+
+bool
+test_01 (int a, int b)
+{
+ return (a ^ b) < 0;
+}
+
+int
+test_02 (int a, int b, int c, int d)
+{
+ if ((a ^ b) < 0)
+ return other_func_a (a, c);
+ else
+ return other_func_b (d, b);
+}
+
+int
+test_03 (int a, int b, int c, int d)
+{
+ if ((a ^ b) >= 0)
+ return other_func_a (a, c);
+ else
+ return other_func_b (d, b);
+}
+
+int
+test_04 (int a, int b)
+{
+ return (a ^ b) >= 0 ? -20 : -40;
+}
+
+bool
+test_05 (int a, int b)
+{
+ return (a ^ b) < 0;
+}
+
+int
+test_06 (int a, int b)
+{
+ return (a ^ b) < 0 ? -20 : -40;
+}
+
+bool
+test_07 (int a, int b)
+{
+ return (a < 0) == (b < 0);
+}
+
+int
+test_08 (int a, int b)
+{
+ return (a < 0) == (b < 0) ? -20 : -40;
+}
+
+bool
+test_09 (int a, int b)
+{
+ return (a < 0) != (b < 0);
+}
+
+int
+test_10 (int a, int b)
+{
+ return (a < 0) != (b < 0) ? -20 : -40;
+}
+
+bool
+test_11 (int a, int b)
+{
+ return (a >= 0) ^ (b < 0);
+}
+
+int
+test_12 (int a, int b)
+{
+ return (a >= 0) ^ (b < 0) ? -20 : -40;
+}
+
+bool
+test_13 (int a, int b)
+{
+ return !((a >= 0) ^ (b < 0));
+}
+
+int
+test_14 (int a, int b)
+{
+ return !((a >= 0) ^ (b < 0)) ? -20 : -40;
+}
+
+bool
+test_15 (int a, int b)
+{
+ return (a & 0x80000000) == (b & 0x80000000);
+}
+
+int
+test_16 (int a, int b)
+{
+ return (a & 0x80000000) == (b & 0x80000000) ? -20 : -40;
+}
+
+bool
+test_17 (int a, int b)
+{
+ return (a & 0x80000000) != (b & 0x80000000);
+}
+
+int
+test_18 (int a, int b)
+{
+ return (a & 0x80000000) != (b & 0x80000000) ? -20 : -40;
+}
+
+int
+test_19 (unsigned int a, unsigned int b)
+{
+ return (a ^ b) >> 31;
+}
+
+int
+test_20 (unsigned int a, unsigned int b)
+{
+ return (a >> 31) ^ (b >> 31);
+}
+
+int
+test_21 (int a, int b)
+{
+ return ((a & 0x80000000) ^ (b & 0x80000000)) >> 31 ? -30 : -10;
+}
+
+int
+test_22 (int a, int b, int c, int d)
+{
+ if ((a < 0) == (b < 0))
+ return other_func_a (a, b);
+ else
+ return other_func_b (c, d);
+}
+
+bool
+test_23 (int a, int b, int c, int d)
+{
+ /* Should emit 2x div0s. */
+ return ((a < 0) == (b < 0)) | ((c < 0) == (d < 0));
+}
Index: gcc/testsuite/gcc.target/sh/pr52933-2.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr52933-2.c (revision 0)
+++ gcc/testsuite/gcc.target/sh/pr52933-2.c (revision 0)
@@ -0,0 +1,12 @@
+/* Check that the div0s instruction is used for integer sign comparisons
+ when -mpretend-cmove is enabled.
+ Each test case is expected to emit at least one div0s insn.
+ Problems when combining the div0s comparison result with surrounding
+ logic usually show up as redundant tst insns. */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2 -mpretend-cmove" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "-m5*" } { "" } } */
+/* { dg-final { scan-assembler-times "div0s" 25 } } */
+/* { dg-final { scan-assembler-not "tst" } } */
+
+#include "pr52933-1.c"
Index: gcc/config/sh/sh.md
===================================================================
--- gcc/config/sh/sh.md (revision 190332)
+++ gcc/config/sh/sh.md (working copy)
@@ -801,6 +801,70 @@
"cmp/pl %0"
[(set_attr "type" "mt_group")])
+;; Some integer sign comparison patterns can be realized with the div0s insn.
+;; div0s Rm,Rn T = (Rm >> 31) ^ (Rn >> 31)
+(define_insn "cmp_div0s_0"
+ [(set (reg:SI T_REG)
+ (lshiftrt:SI (xor:SI (match_operand:SI 0 "arith_reg_operand" "%r")
+ (match_operand:SI 1 "arith_reg_operand" "r"))
+ (const_int 31)))]
+ "TARGET_SH1"
+ "div0s %0,%1"
+ [(set_attr "type" "arith")])
+
+(define_insn "cmp_div0s_1"
+ [(set (reg:SI T_REG)
+ (lt:SI (xor:SI (match_operand:SI 0 "arith_reg_operand" "%r")
+ (match_operand:SI 1 "arith_reg_operand" "r"))
+ (const_int 0)))]
+ "TARGET_SH1"
+ "div0s %0,%1"
+ [(set_attr "type" "arith")])
+
+(define_insn_and_split "*cmp_div0s_0"
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (lshiftrt:SI (xor:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "arith_reg_operand" ""))
+ (const_int 31)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (lshiftrt:SI (xor:SI (match_dup 1) (match_dup 2)) (const_int 31)))
+ (set (match_dup 0) (reg:SI T_REG))])
+
+(define_insn_and_split "*cmp_div0s_1"
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (ge:SI (xor:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "arith_reg_operand" ""))
+ (const_int 0)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "#"
+ "&& can_create_pseudo_p ()"
+ [(const_int 0)]
+;; We have to go through the movnegt expander here which will handle the
+;; SH2A vs non-SH2A cases.
+{
+ emit_insn (gen_cmp_div0s_1 (operands[1], operands[2]));
+ emit_insn (gen_movnegt (operands[0], get_t_reg_rtx ()));
+ DONE;
+})
+
+(define_insn_and_split "*cmp_div0s_1"
+ [(set (reg:SI T_REG)
+ (ge:SI (xor:SI (match_operand:SI 0 "arith_reg_operand" "")
+ (match_operand:SI 1 "arith_reg_operand" ""))
+ (const_int 0)))]
+ "TARGET_SH1"
+ "#"
+ "&& can_create_pseudo_p ()"
+ [(set (reg:SI T_REG) (lt:SI (xor:SI (match_dup 0) (match_dup 1))
+ (const_int 0)))
+ (set (reg:SI T_REG) (xor:SI (reg:SI T_REG) (const_int 1)))])
+
+
;; -------------------------------------------------------------------------
;; SImode compare and branch
;; -------------------------------------------------------------------------
@@ -918,6 +982,63 @@
(label_ref (match_dup 2))
(pc)))])
+;; Compare and branch combine patterns for div0s comparisons.
+(define_insn_and_split "*cbranch_div0s"
+ [(set (pc)
+ (if_then_else (lt (xor:SI (match_operand:SI 0 "arith_reg_operand" "")
+ (match_operand:SI 1 "arith_reg_operand" ""))
+ (const_int 0))
+ (label_ref (match_operand 2))
+ (pc)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (lt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 0)))
+ (set (pc)
+ (if_then_else (ne (reg:SI T_REG) (const_int 0))
+ (label_ref (match_dup 2))
+ (pc)))])
+
+(define_insn_and_split "*cbranch_div0s"
+ [(set (pc)
+ (if_then_else (ge (xor:SI (match_operand:SI 0 "arith_reg_operand" "")
+ (match_operand:SI 1 "arith_reg_operand" ""))
+ (const_int 0))
+ (label_ref (match_operand 2))
+ (pc)))
+ (clobber (reg:SI T_REG))]
+ "TARGET_SH1"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG)
+ (lt:SI (xor:SI (match_dup 0) (match_dup 1)) (const_int 0)))
+ (set (pc)
+ (if_then_else (eq (reg:SI T_REG) (const_int 0))
+ (label_ref (match_dup 2))
+ (pc)))])
+
+;; Conditional move combine pattern for div0s comparisons.
+;; This is used when TARGET_PRETEND_CMOVE is in effect.
+(define_insn_and_split "*movsicc_div0s"
+ [(set (match_operand:SI 0 "arith_reg_dest" "")
+ (if_then_else:SI (ge (xor:SI (match_operand:SI 1 "arith_reg_operand" "")
+ (match_operand:SI 2 "arith_reg_operand" ""))
+ (const_int 0))
+ (match_operand:SI 3 "arith_reg_operand" "")
+ (match_operand:SI 4 "general_movsrc_operand" "")))
+ (clobber (reg:SI T_REG))]
+ "TARGET_PRETEND_CMOVE"
+ "#"
+ "&& 1"
+ [(set (reg:SI T_REG) (lt:SI (xor:SI (match_dup 1) (match_dup 2))
+ (const_int 0)))
+ (set (match_dup 0)
+ (if_then_else (ne (reg:SI T_REG) (const_int 0))
+ (match_dup 4)
+ (match_dup 3)))])
+
;; -------------------------------------------------------------------------
;; SImode unsigned integer comparisons
;; -------------------------------------------------------------------------
Index: gcc/config/sh/sh.c
===================================================================
--- gcc/config/sh/sh.c (revision 190332)
+++ gcc/config/sh/sh.c (working copy)
@@ -3186,9 +3186,33 @@
*total = COSTS_N_INSNS (multcosts (x));
return true;
+ case LT:
+ case GE:
+ /* div0s sign comparison. */
+ if (GET_CODE (XEXP (x, 0)) == XOR
+ && REG_P ((XEXP (XEXP (x, 0), 0)))
+ && REG_P ((XEXP (XEXP (x, 0), 1)))
+ && satisfies_constraint_Z (XEXP (x, 1)))
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ else
+ return false;
+
+ case LSHIFTRT:
+ /* div0s sign comparison. */
+ if (GET_CODE (XEXP (x, 0)) == XOR
+ && REG_P ((XEXP (XEXP (x, 0), 0)))
+ && REG_P ((XEXP (XEXP (x, 0), 1)))
+ && CONST_INT_P (XEXP (x, 1)) && INTVAL (XEXP (x, 1)) == 31)
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ /* Fall through to shiftcosts. */
case ASHIFT:
case ASHIFTRT:
- case LSHIFTRT:
{
int cost = shiftcosts (x);
if (cost < 0)