Patchwork [SH] PR 55303 - Add basic support for SH2A clip insns

login
register
mail settings
Submitter Oleg Endo
Date March 5, 2013, 8:51 p.m.
Message ID <1362516707.2219.30.camel@yam-132-YW-E178-FTW>
Download mbox | patch
Permalink /patch/225173/
State New
Headers show

Comments

Oleg Endo - March 5, 2013, 8:51 p.m.
Hi,

This adds basic support for the SH2A clips and clipu instructions.
Tested on rev 196406 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 for trunk or 4.9?

Cheers,
Oleg

gcc/ChangeLog:

	PR target/55303
	* config/sh/sh.c (sh_rtx_costs): Handle SMIN and SMAX cases.
	* config/sh/sh.md (*clips, uminsi3, *clipu, clipu_one): New 
	insns and related expanders.
	* config/sh/iterators.md (SMIN_SMAX): New code iterator.
	* config/sh/predicates.md (arith_reg_or_0_or_1_operand, 
	clips_min_const_int, clips_max_const_int, clipu_max_const_int):
	New predicates.

testsuite/ChangeLog:

	PR target/55303
	* gcc.target/sh/pr55303-1.c: New.
	* gcc.target/sh/pr55303-2.c: New.
	* gcc.target/sh/pr55303-3.c: New.
Kaz Kojima - March 5, 2013, 10:37 p.m.
Oleg Endo <oleg.endo@t-online.de> wrote:
> This adds basic support for the SH2A clips and clipu instructions.
> Tested on rev 196406 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 for trunk or 4.9?

OK.

Regards,
	kaz
Oleg Endo - March 5, 2013, 10:39 p.m.
On Wed, 2013-03-06 at 07:37 +0900, Kaz Kojima wrote:
> Oleg Endo <oleg.endo@t-online.de> wrote:
> > This adds basic support for the SH2A clips and clipu instructions.
> > Tested on rev 196406 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 for trunk or 4.9?
> 
> OK.

OK for 4.8 trunk or 4.9? :)

Cheers,
Oleg
Kaz Kojima - March 5, 2013, 11:17 p.m.
Oleg Endo <oleg.endo@t-online.de> wrote:
> OK for 4.8 trunk or 4.9? :)

Sorry, I've missed the trunk part.  OK for 4.9.

Regards,
	kaz

Patch

Index: gcc/testsuite/gcc.target/sh/pr55303-1.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr55303-1.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/pr55303-1.c	(revision 0)
@@ -0,0 +1,87 @@ 
+/* Verify that the SH2A clips and clipu instructions are generated as
+   expected.  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "*" } { "-m2a*" } } */
+/* { dg-final { scan-assembler-times "clips.b" 2 } } */
+/* { dg-final { scan-assembler-times "clips.w" 2 } } */
+/* { dg-final { scan-assembler-times "clipu.b" 2 } } */
+/* { dg-final { scan-assembler-times "clipu.w" 2 } } */
+
+static inline int
+min (int a, int b)
+{
+  return a < b ? a : b;
+}
+
+static inline int
+max (int a, int b)
+{
+  return a < b ? b : a;
+}
+
+int
+test_00 (int a)
+{
+  /* 1x clips.b  */
+  return max (-128, min (127, a));
+}
+
+int
+test_01 (int a)
+{
+  /* 1x clips.b  */
+  return min (127, max (-128, a));
+}
+
+int
+test_02 (int a)
+{
+  /* 1x clips.w  */
+  return max (-32768, min (32767, a));
+}
+
+int
+test_03 (int a)
+{
+  /* 1x clips.w  */
+  return min (32767, max (-32768, a));
+}
+
+unsigned int
+test_04 (unsigned int a)
+{
+  /* 1x clipu.b  */
+  return a > 255 ? 255 : a;
+}
+
+unsigned int
+test_05 (unsigned int a)
+{
+  /* 1x clipu.b  */
+  return a >= 255 ? 255 : a;
+}
+
+unsigned int
+test_06 (unsigned int a)
+{
+  /* 1x clipu.w  */
+  return a > 65535 ? 65535 : a;
+}
+
+unsigned int
+test_07 (unsigned int a)
+{
+  /* 1x clipu.w  */
+  return a >= 65535 ? 65535 : a;
+}
+
+void
+test_08 (unsigned short a, unsigned short b, unsigned int* r)
+{
+  /* Must not see a clip insn here -- it is not needed.  */
+  unsigned short x = a + b;
+  if (x > 65535)
+    x = 65535;
+  *r = x;
+}
Index: gcc/testsuite/gcc.target/sh/pr55303-3.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr55303-3.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/pr55303-3.c	(revision 0)
@@ -0,0 +1,15 @@ 
+/* Verify that the special case (umin (reg const_int 1)) results in the
+   expected instruction sequence on SH2A.  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "*" } { "-m2a*" } } */
+/* { dg-final { scan-assembler-times "tst" 1 } } */
+/* { dg-final { scan-assembler-times "movrt" 1 } } */
+
+unsigned int
+test_00 (unsigned int a)
+{
+  /* 1x tst
+     1x movrt  */
+  return a > 1 ? 1 : a;
+}
Index: gcc/testsuite/gcc.target/sh/pr55303-2.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr55303-2.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/pr55303-2.c	(revision 0)
@@ -0,0 +1,35 @@ 
+/* Verify that for SH2A smax/smin -> cbranch conversion is done properly
+   if the clips insn is not used and the expected comparison insns are
+   generated.  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "*" } { "-m2a*" } } */
+/* { dg-final { scan-assembler-times "cmp/pl" 4 } } */
+
+int
+test_00 (int a)
+{
+  /* 1x cmp/pl  */
+  return a >= 0 ? a : 0;
+}
+
+int
+test_01 (int a)
+{
+  /* 1x cmp/pl  */
+  return a <= 0 ? a : 0;
+}
+
+int
+test_02 (int a)
+{
+  /* 1x cmp/pl  */
+  return a < 1 ? 1 : a;
+}
+
+int
+test_03 (int a)
+{
+  /* 1x cmp/pl  */
+  return a < 1 ? a : 1;
+}
Index: gcc/config/sh/sh.c
===================================================================
--- gcc/config/sh/sh.c	(revision 196091)
+++ gcc/config/sh/sh.c	(working copy)
@@ -3507,6 +3507,22 @@ 
       else
 	return false;
 
+    case SMIN:
+    case SMAX:
+      /* This is most likely a clips.b or clips.w insn that is being made up
+	 by combine.  */
+      if (TARGET_SH2A
+	  && (GET_CODE (XEXP (x, 0)) == SMAX || GET_CODE (XEXP (x, 0)) == SMIN)
+	  && CONST_INT_P (XEXP (XEXP (x, 0), 1))
+	  && REG_P (XEXP (XEXP (x, 0), 0))
+	  && CONST_INT_P (XEXP (x, 1)))
+	{
+	  *total = COSTS_N_INSNS (1);
+	  return true;
+	}
+      else
+	return false;
+
     case CONST:
     case LABEL_REF:
     case SYMBOL_REF:
Index: gcc/config/sh/sh.md
===================================================================
--- gcc/config/sh/sh.md	(revision 196091)
+++ gcc/config/sh/sh.md	(working copy)
@@ -11709,6 +11709,140 @@ 
    (set_attr "in_delay_slot" "no")])
 
 ;; -------------------------------------------------------------------------
+;; Minimum / maximum operations.
+;; -------------------------------------------------------------------------
+
+;; The SH2A clips.b and clips.w insns do a signed min-max function.  If smin
+;; and smax standard name patterns are defined, they will be used during
+;; initial expansion and combine will then be able to form the actual min-max
+;; pattern.
+;; The clips.b and clips.w set the SR.CS bit if the value in the register is
+;; clipped, but there is currently no way of making use of this information.
+;; The only way to read or reset the SR.CS bit is by accessing the SR.
+(define_expand "<code>si3"
+  [(parallel [(set (match_operand:SI 0 "arith_reg_dest")
+		   (SMIN_SMAX:SI (match_operand:SI 1 "arith_reg_operand")
+				 (match_operand 2 "const_int_operand")))
+	      (clobber (reg:SI T_REG))])]
+  "TARGET_SH2A"
+{
+  /* Force the comparison value into a register, because greater-than
+     comparisons can work only on registers.  Combine will be able to pick up
+     the constant value from the REG_EQUAL note when trying to form a min-max
+     pattern.  */
+  operands[2] = force_reg (SImode, operands[2]);
+})
+
+;; Convert
+;;	smax (smin (...))
+;; to
+;;	smin (smax (...))
+(define_insn_and_split "*clips"
+  [(set (match_operand:SI 0 "arith_reg_dest")
+	(smax:SI (smin:SI (match_operand:SI 1 "arith_reg_operand")
+			  (match_operand 2 "clips_max_const_int"))
+		 (match_operand 3 "clips_min_const_int")))]
+  "TARGET_SH2A"
+  "#"
+  "&& 1"
+  [(set (match_dup 0)
+	(smin:SI (smax:SI (match_dup 1) (match_dup 3)) (match_dup 2)))])
+
+(define_insn "*clips"
+  [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+	(smin:SI (smax:SI (match_operand:SI 1 "arith_reg_operand" "0")
+			  (match_operand 2 "clips_min_const_int"))
+		 (match_operand 3 "clips_max_const_int")))]
+  "TARGET_SH2A"
+{
+  if (INTVAL (operands[3]) == 127)
+    return "clips.b	%0";
+  else if (INTVAL (operands[3]) == 32767)
+    return "clips.w	%0";
+  else
+    gcc_unreachable ();
+}
+  [(set_attr "type" "arith")])
+
+;; If the expanded smin or smax patterns were not combined, split them into
+;; a compare and branch sequence, because there are no real smin or smax
+;; insns.
+(define_insn_and_split "*<code>si3"
+  [(set (match_operand:SI 0 "arith_reg_dest")
+	(SMIN_SMAX:SI (match_operand:SI 1 "arith_reg_operand")
+		      (match_operand:SI 2 "arith_reg_or_0_or_1_operand")))
+   (clobber (reg:SI T_REG))]
+  "TARGET_SH2A && can_create_pseudo_p ()"
+  "#"
+  "&& 1"
+  [(const_int 0)]
+{
+  rtx skip_label = gen_label_rtx ();
+  emit_move_insn (operands[0], operands[1]);
+
+  rtx cmp_val = operands[2];
+  if (satisfies_constraint_M (cmp_val))
+    cmp_val = const0_rtx;
+
+  emit_insn (gen_cmpgtsi_t (operands[0], cmp_val));
+  emit_jump_insn (<CODE> == SMIN
+			    ? gen_branch_false (skip_label)
+			    : gen_branch_true (skip_label));
+
+  emit_label_after (skip_label, emit_move_insn (operands[0], operands[2]));
+  DONE;
+})
+
+;; The SH2A clipu.b and clipu.w insns can be used to implement a min function
+;; with a register and a constant.
+;; The clipu.b and clipu.w set the SR.CS bit if the value in the register is
+;; clipped, but there is currently no way of making use of this information.
+;; The only way to read or reset the SR.CS bit is by accessing the SR.
+(define_expand "uminsi3"
+  [(set (match_operand:SI 0 "arith_reg_dest")
+	(umin:SI (match_operand:SI 1 "arith_reg_operand")
+		 (match_operand 2 "const_int_operand")))]
+  "TARGET_SH2A"
+{
+  if (INTVAL (operands[2]) == 1)
+    {
+      emit_insn (gen_clipu_one (operands[0], operands[1]));
+      DONE;
+    }
+  else if (! clipu_max_const_int (operands[2], VOIDmode))
+    FAIL;
+})
+
+(define_insn "*clipu"
+  [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+	(umin:SI (match_operand:SI 1 "arith_reg_operand" "0")
+		 (match_operand 2 "clipu_max_const_int")))]
+  "TARGET_SH2A"
+{
+  if (INTVAL (operands[2]) == 255)
+    return "clipu.b	%0";
+  else if (INTVAL (operands[2]) == 65535)
+    return "clipu.w	%0";
+  else
+    gcc_unreachable ();
+}
+  [(set_attr "type" "arith")])
+
+(define_insn_and_split "clipu_one"
+  [(set (match_operand:SI 0 "arith_reg_dest")
+	(umin:SI (match_operand:SI 1 "arith_reg_operand") (const_int 1)))
+   (clobber (reg:SI T_REG))]
+  "TARGET_SH2A"
+  "#"
+  "&& can_create_pseudo_p ()"
+  [(const_int 0)]
+{
+  emit_insn (gen_cmpeqsi_t (operands[1], const0_rtx));
+  emit_insn (gen_movnegt (operands[0], get_t_reg_rtx ()));
+  DONE;
+})
+
+;; -------------------------------------------------------------------------
 ;; Misc
 ;; -------------------------------------------------------------------------
 
Index: gcc/config/sh/iterators.md
===================================================================
--- gcc/config/sh/iterators.md	(revision 196091)
+++ gcc/config/sh/iterators.md	(working copy)
@@ -41,3 +41,6 @@ 
 ;; Lowpart subreg byte position code attributes for big and little endian.
 (define_mode_attr lowpart_be [(QI "3") (HI "2")])
 (define_mode_attr lowpart_le [(QI "0") (HI "0")])
+
+;; Signed minimum/maximum code iterator.
+(define_code_iterator SMIN_SMAX [smin smax])
Index: gcc/config/sh/predicates.md
===================================================================
--- gcc/config/sh/predicates.md	(revision 196091)
+++ gcc/config/sh/predicates.md	(working copy)
@@ -195,6 +195,34 @@ 
   return 0;
 })
 
+;; Returns true if OP is either a register or constant 0 or constant 1.
+(define_predicate "arith_reg_or_0_or_1_operand"
+  (match_code "subreg,reg,const_int,const_vector")
+{
+  return arith_reg_or_0_operand (op, mode) || satisfies_constraint_M (op);
+})
+
+;; Returns true if OP is a suitable constant for the minimum value of a
+;; clips.b or clips.w insn.
+(define_predicate "clips_min_const_int"
+  (and (match_code "const_int")
+       (ior (match_test "INTVAL (op) == -128")
+	    (match_test "INTVAL (op) == -32768"))))
+
+;; Returns true if OP is a suitable constant for the maximum value of a
+;; clips.b or clips.w insn.
+(define_predicate "clips_max_const_int"
+  (and (match_code "const_int")
+       (ior (match_test "INTVAL (op) == 127")
+	    (match_test "INTVAL (op) == 32767"))))
+
+;; Returns true if OP is a suitable constant for the maximum value of a
+;; clipu.b or clipu.w insn.
+(define_predicate "clipu_max_const_int"
+  (and (match_code "const_int")
+       (ior (match_test "INTVAL (op) == 255")
+	    (match_test "INTVAL (op) == 65535"))))
+
 ;; Returns 1 if OP is a floating point operator with two operands.
 (define_predicate "binary_float_operator"
   (and (match_code "plus,minus,mult,div")