Patchwork [SH] PR 52933 - Use div0s insn for integer sign comparisons

login
register
mail settings
Submitter Oleg Endo
Date Aug. 13, 2012, 8:08 p.m.
Message ID <1344888532.2279.51.camel@yam-132-YW-E178-FTW>
Download mbox | patch
Permalink /patch/177058/
State New
Headers show

Comments

Oleg Endo - Aug. 13, 2012, 8:08 p.m.
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.
Kaz Kojima - Aug. 14, 2012, 11:30 a.m.
Oleg Endo <oleg.endo@t-online.de> wrote:
> 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?
 
OK.

Regards,
	kaz

Patch

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)