Hello,

This adapts SH's logical right shift patterns to work/look the same way
as left shift patterns.  It mainly fixes a few issues with dynamic shift
insn selection.
Tested on rev 190580 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/54089
        * config/sh/predicates (p27_rshift_count_operand, 
        not_p27_rshift_count_operand): New predicates.
        * config/sh/sh.c (sh_ashlsi_clobbers_t_reg_p,
        sh_lshrsi_clobbers_t_reg_p, sh_dynamicalize_shift_p): Handle 
        special case when shift amount is 31.
        (gen_ashift): Emit gen_shlr instead of gen_lshrsi3_m.
        * config/sh/sh.md (ashlsi3_d): Set type to 'dyn_shift' instead 
        of 'arith'.
        (ashlsi_c): Rename to shll.  Adapt calls to gen_ashlsi_c
        throughout the file.
        (lshrsi3): Remove clobber from expander.  Use 
        shift_count_operand instead of nonmemory_operand predicate for 
        second operand.  Add handling of case lshrsi3_n_clobbers_t.
        (lshrsi3_k): Use p27_rshift_count_operand for second operand.
        (lshrsi3_d): Make insn_and_split.  Split dynamic shift to 
        constant shift sequences if beneficial.
        (lshrsi3_n): Make insn_and_split.  Split constant shift sequence
        to dynamic shift if beneficial.
        (lshrsi3_n_clobbers_t): New insn_and_split.
        (lshrsi3_m): Delete.
        

testsuite/ChangeLog:

        PR target/54089
        * gcc.target/sh/pr54089-2.c: New.
Index: gcc/config/sh/predicates.md
===================================================================
--- gcc/config/sh/predicates.md	(revision 190580)
+++ gcc/config/sh/predicates.md	(working copy)
@@ -825,6 +825,8 @@
   return arith_reg_operand (op, mode);
 })
 
+;; Predicates for matching operands that are constant shift
+;; amounts 1, 2, 8, 16.
 (define_predicate "p27_shift_count_operand"
   (and (match_code "const_int")
        (match_test "satisfies_constraint_P27 (op)")))
@@ -833,6 +835,19 @@
   (and (match_code "const_int")
        (match_test "! satisfies_constraint_P27 (op)")))
 
+;; For right shifts the constant 1 is a special case because the shlr insn
+;; clobbers the T_REG and is handled by the T_REG clobbering version of the
+;; insn, which is also used for non-P27 shift sequences.
+(define_predicate "p27_rshift_count_operand"
+  (and (match_code "const_int")
+       (match_test "satisfies_constraint_P27 (op)")
+       (match_test "! satisfies_constraint_M (op)")))
+
+(define_predicate "not_p27_rshift_count_operand"
+  (and (match_code "const_int")
+       (ior (match_test "! satisfies_constraint_P27 (op)")
+	    (match_test "satisfies_constraint_M (op)"))))
+
 ;; TODO: Add a comment here.
 
 (define_predicate "shift_operator"
Index: gcc/config/sh/sh.c
===================================================================
--- gcc/config/sh/sh.c	(revision 190580)
+++ gcc/config/sh/sh.c	(working copy)
@@ -2840,6 +2840,11 @@
   { 4, { 16, 8, 2, 2 },	    0 },
   { 4, { 16, -1, -2, 16 },  ASHL_CLOBBERS_T },
   { 3, { 16, -2, 16 },	    0 },
+
+  /* For a right shift by 31 a 2 insn shll-movt sequence can be used.
+     For a left shift by 31 a 2 insn and-rotl sequences can be used.
+     However, the shift-and combiner code needs this entry here to be in
+     terms of real shift insns.  */
   { 3, { 16, -1, 16 },	    ASHL_CLOBBERS_T }
 };
 
@@ -2888,7 +2893,14 @@
 sh_ashlsi_clobbers_t_reg_p (rtx shift_amount)
 {
   gcc_assert (CONST_INT_P (shift_amount));
-  return (ashl_lshr_seq[INTVAL (shift_amount) & 31].clobbers_t
+  
+  const int shift_amount_i = INTVAL (shift_amount) & 31;
+
+  /* Special case for shift count of 31: use and-rotl sequence.  */
+  if (shift_amount_i == 31)
+    return true;
+
+  return (ashl_lshr_seq[shift_amount_i].clobbers_t
 	  & ASHL_CLOBBERS_T) != 0;
 }
 
@@ -2896,10 +2908,39 @@
 sh_lshrsi_clobbers_t_reg_p (rtx shift_amount)
 {
   gcc_assert (CONST_INT_P (shift_amount));
-  return (ashl_lshr_seq[INTVAL (shift_amount) & 31].clobbers_t
+
+  const int shift_amount_i = INTVAL (shift_amount) & 31;
+ 
+  /* Special case for shift count of 31: use shll-movt sequence.  */
+  if (shift_amount_i == 31)
+    return true;
+
+  return (ashl_lshr_seq[shift_amount_i].clobbers_t
 	  & LSHR_CLOBBERS_T) != 0;
 }
 
+/* Return true if it is potentially beneficial to use a dynamic shift
+   instruction (shad / shar) instead of a combination of 1/2/8/16 
+   shift instructions for the specified shift count.
+   If dynamic shifts are not available, always return false.  */
+bool
+sh_dynamicalize_shift_p (rtx count)
+{
+  gcc_assert (CONST_INT_P (count));
+
+  const int shift_amount_i = INTVAL (count) & 31;
+  int insn_count;
+
+  /* For left and right shifts, there are shorter 2 insn sequences for
+     shift amounts of 31.  */
+  if (shift_amount_i == 31)
+    insn_count = 2;
+  else
+    insn_count = ashl_lshr_seq[shift_amount_i].insn_count;
+
+  return TARGET_DYNSHIFT && (insn_count > 1 + SH_DYNAMIC_SHIFT_COST);
+}
+
 /* Assuming we have a value that has been sign-extended by at least one bit,
    can we use the ext_shift_amounts with the last shift turned to an arithmetic shift
    to shift it by N without data loss, and quicker than by other means?  */
@@ -3385,7 +3426,7 @@
       break;
     case LSHIFTRT:
       if (n == 1)
-	emit_insn (gen_lshrsi3_m (reg, reg, n_rtx));
+        emit_insn (gen_shlr (reg, reg));
       else
 	emit_insn (gen_lshrsi3_k (reg, reg, n_rtx));
       break;
@@ -3596,19 +3637,6 @@
   return true;
 }
 
-/* Return true if it is potentially beneficial to use a dynamic shift
-   instruction (shad / shar) instead of a combination of 1/2/8/16 
-   shift instructions for the specified shift count.
-   If dynamic shifts are not available, always return false.  */
-bool
-sh_dynamicalize_shift_p (rtx count)
-{
-  int insn_count;
-  gcc_assert (CONST_INT_P (count));
-  insn_count = ashl_lshr_seq[INTVAL (count) & 31].insn_count;
-  return TARGET_DYNSHIFT && (insn_count > 1 + SH_DYNAMIC_SHIFT_COST);
-}
-
 /* Try to find a good way to implement the combiner pattern
   [(set (match_operand:SI 0 "register_operand" "r")
         (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
Index: gcc/config/sh/sh.md
===================================================================
--- gcc/config/sh/sh.md	(revision 190580)
+++ gcc/config/sh/sh.md	(working copy)
@@ -4058,7 +4058,7 @@
 
   FAIL;
 }
-  [(set_attr "type" "arith")])
+  [(set_attr "type" "dyn_shift")])
 
 (define_insn_and_split "ashlsi3_n"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
@@ -4116,7 +4116,7 @@
   DONE;
 })
 
-(define_insn "ashlsi_c"
+(define_insn "shll"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
 	(ashift:SI (match_operand:SI 1 "arith_reg_operand" "0") (const_int 1)))
    (set (reg:SI T_REG)
@@ -4142,7 +4142,7 @@
    && peep2_reg_dead_p (2, operands[1])"
   [(const_int 0)]
 {
-  emit_insn (gen_ashlsi_c (operands[1], operands[1]));
+  emit_insn (gen_shll (operands[1], operands[1]));
   DONE;
 })
 
@@ -4349,7 +4349,7 @@
   "&& 1"
   [(const_int 0)]
 {
-  emit_insn (gen_ashlsi_c (operands[0], operands[1]));
+  emit_insn (gen_shll (operands[0], operands[1]));
   emit_insn (gen_mov_neg_si_t (operands[0], get_t_reg_rtx ()));
   DONE;
 })
@@ -4463,12 +4463,10 @@
 ;; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 ;; SImode logical shift right
 
-;; Only the single bit shift clobbers the T bit.
 (define_expand "lshrsi3"
-  [(parallel [(set (match_operand:SI 0 "arith_reg_dest" "")
-		   (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
-				(match_operand:SI 2 "nonmemory_operand" "")))
-	      (clobber (reg:SI T_REG))])]
+  [(set (match_operand:SI 0 "arith_reg_dest" "")
+	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "")
+		     (match_operand:SI 2 "shift_count_operand" "")))]
   ""
 {
   if (TARGET_SHMEDIA)
@@ -4476,71 +4474,147 @@
       emit_insn (gen_lshrsi3_media (operands[0], operands[1], operands[2]));
       DONE;
     }
-  if (CONST_INT_P (operands[2])
-      && sh_dynamicalize_shift_p (operands[2]))
-    operands[2] = force_reg (SImode, operands[2]);
+
+  /* If a dynamic shift is supposed to be used, expand the lshrsi3_d insn
+     here, otherwise the pattern will never match due to the shift amount reg
+     negation.  */
   if (TARGET_DYNSHIFT
-      && arith_reg_operand (operands[2], GET_MODE (operands[2])))
+      && CONST_INT_P (operands[2]) && sh_dynamicalize_shift_p (operands[2]))
     {
-      rtx count = copy_to_mode_reg (SImode, operands[2]);
-      emit_insn (gen_negsi2 (count, count));
-      emit_insn (gen_lshrsi3_d (operands[0], operands[1], count));
+      rtx neg_count = force_reg (SImode,
+			         gen_int_mode (- INTVAL (operands[2]), SImode));
+      emit_insn (gen_lshrsi3_d (operands[0], operands[1], neg_count));
       DONE;
     }
-  if (! immediate_operand (operands[2], GET_MODE (operands[2])))
-    FAIL;
+
+  if (TARGET_DYNSHIFT && ! CONST_INT_P (operands[2]))
+    {
+      rtx neg_count = gen_reg_rtx (SImode);
+      emit_insn (gen_negsi2 (neg_count, operands[2]));
+      emit_insn (gen_lshrsi3_d (operands[0], operands[1], neg_count));
+      DONE;
+    }
+
+  /* If the lshrsi3_* insn is going to clobber the T_REG it must be
+     expanded here.  */
+  if (CONST_INT_P (operands[2])
+      && sh_lshrsi_clobbers_t_reg_p (operands[2])
+      && ! sh_dynamicalize_shift_p (operands[2]))
+    {
+      emit_insn (gen_lshrsi3_n_clobbers_t (operands[0], operands[1],
+		 operands[2]));
+      DONE;
+    }
 })
 
-(define_insn "lshrsi3_d"
+(define_insn "lshrsi3_k"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
 	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
-		     (neg:SI (match_operand:SI 2 "arith_reg_operand" "r"))))]
-  "TARGET_DYNSHIFT"
-  "shld	%2,%0"
-  [(set_attr "type" "dyn_shift")])
-
-(define_insn "shlr"
-  [(set (match_operand:SI 0 "arith_reg_dest" "=r")
-	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
-		     (const_int 1)))
-   (set (reg:SI T_REG)
-	(and:SI (match_dup 1) (const_int 1)))]
+		     (match_operand:SI 2 "p27_rshift_count_operand" "P27")))]
   "TARGET_SH1"
-  "shlr	%0"
+  "shlr%O2	%0"
   [(set_attr "type" "arith")])
 
-(define_insn "lshrsi3_m"
+(define_insn_and_split "lshrsi3_d"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
 	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
-		     (match_operand:SI 2 "const_int_operand" "M")))
-   (clobber (reg:SI T_REG))]
-  "TARGET_SH1 && satisfies_constraint_M (operands[2])"
-  "shlr	%0"
-  [(set_attr "type" "arith")])
+		     (neg:SI (match_operand:SI 2 "shift_count_operand" "r"))))]
+  "TARGET_DYNSHIFT"
+  "shld	%2,%0"
+  "&& CONST_INT_P (operands[2]) && ! sh_dynamicalize_shift_p (operands[2])
+   && ! sh_lshrsi_clobbers_t_reg_p (operands[2])"
+  [(const_int 0)]
+{
+  if (satisfies_constraint_P27 (operands[2]))
+    {
+      /* This will not be done for a shift amount of 1, because it would
+	 clobber the T_REG.  */
+      emit_insn (gen_lshrsi3_k (operands[0], operands[1], operands[2]));
+      DONE;
+    }
+  else if (! satisfies_constraint_P27 (operands[2]))
+    {
+      /* This must happen before reload, otherwise the constant will be moved
+	 into a register due to the "r" constraint, after which this split
+	 cannot be done anymore.
+	 Unfortunately the move insn will not always be eliminated.
+	 Also, here we must not create a shift sequence that clobbers the
+	 T_REG.  */
+      emit_move_insn (operands[0], operands[1]);
+      gen_shifty_op (LSHIFTRT, operands);
+      DONE;
+    }
 
-(define_insn "lshrsi3_k"
+  FAIL;
+}
+  [(set_attr "type" "dyn_shift")])
+
+(define_insn_and_split "lshrsi3_n"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
 	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
-		     (match_operand:SI 2 "const_int_operand" "P27")))]
-  "TARGET_SH1 && satisfies_constraint_P27 (operands[2])
-   && ! satisfies_constraint_M (operands[2])"
-  "shlr%O2	%0"
-  [(set_attr "type" "arith")])
+		     (match_operand:SI 2 "not_p27_rshift_count_operand")))]
+  "TARGET_SH1 && ! sh_lshrsi_clobbers_t_reg_p (operands[2])"
+  "#"
+  "&& (reload_completed
+       || (sh_dynamicalize_shift_p (operands[2]) && can_create_pseudo_p ()))"
+  [(const_int 0)]
+{
+  if (sh_dynamicalize_shift_p (operands[2]) && can_create_pseudo_p ())
+    {
+      /* If this pattern was picked and dynamic shifts are supported, switch
+	 to dynamic shift pattern before reload.  */
+      operands[2] = force_reg (SImode,
+			       gen_int_mode (- INTVAL (operands[2]), SImode));
+      emit_insn (gen_lshrsi3_d (operands[0], operands[1], operands[2]));
+    }
+  else
+    gen_shifty_op (LSHIFTRT, operands);
 
-(define_insn_and_split "lshrsi3_n"
+  DONE;
+})
+
+;; The lshrsi3_n_clobbers_t pattern also works as a simplified version of
+;; the shlr pattern.
+(define_insn_and_split "lshrsi3_n_clobbers_t"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r")
 	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
-		     (match_operand:SI 2 "const_int_operand" "n")))
+		     (match_operand:SI 2 "not_p27_rshift_count_operand")))
    (clobber (reg:SI T_REG))]
-  "TARGET_SH1 && ! sh_dynamicalize_shift_p (operands[2])"
+  "TARGET_SH1 && sh_lshrsi_clobbers_t_reg_p (operands[2])"
   "#"
-  "TARGET_SH1 && reload_completed"
-  [(use (reg:SI R0_REG))]
+  "&& (reload_completed || INTVAL (operands[2]) == 31
+       || (sh_dynamicalize_shift_p (operands[2]) && can_create_pseudo_p ()))"
+  [(const_int 0)]
 {
-  gen_shifty_op (LSHIFTRT, operands);
+  if (INTVAL (operands[2]) == 31)
+    {
+      emit_insn (gen_shll (operands[0], operands[1]));
+      emit_insn (gen_movt (operands[0], get_t_reg_rtx ()));
+    }
+  else if (sh_dynamicalize_shift_p (operands[2]) && can_create_pseudo_p ())
+    {
+      /* If this pattern was picked and dynamic shifts are supported, switch
+	 to dynamic shift pattern before reload.  */
+      operands[2] = force_reg (SImode,
+			       gen_int_mode (- INTVAL (operands[2]), SImode));
+      emit_insn (gen_lshrsi3_d (operands[0], operands[1], operands[2]));
+    }
+  else
+    gen_shifty_op (LSHIFTRT, operands);
+
   DONE;
 })
 
+(define_insn "shlr"
+  [(set (match_operand:SI 0 "arith_reg_dest" "=r")
+	(lshiftrt:SI (match_operand:SI 1 "arith_reg_operand" "0")
+		     (const_int 1)))
+   (set (reg:SI T_REG)
+	(and:SI (match_dup 1) (const_int 1)))]
+  "TARGET_SH1"
+  "shlr	%0"
+  [(set_attr "type" "arith")])
+
 (define_insn "lshrsi3_media"
   [(set (match_operand:SI 0 "arith_reg_dest" "=r,r")
 	(lshiftrt:SI (match_operand:SI 1 "extend_reg_operand" "r,r")
Index: gcc/testsuite/gcc.target/sh/pr54089-2.c
===================================================================
--- gcc/testsuite/gcc.target/sh/pr54089-2.c	(revision 0)
+++ gcc/testsuite/gcc.target/sh/pr54089-2.c	(revision 0)
@@ -0,0 +1,22 @@
+/* Check that for dynamic logical right shifts with a constant the negated
+   constant is loaded directly, instead of loading the postitive constant
+   and negating it separately.  This was a case that happened at optimization
+   level -O2 and looked like:
+	cmp/eq	r6,r5
+	mov	#30,r1
+	neg	r1,r1
+	shld	r1,r4
+	mov	r4,r0
+	rts
+	rotcr	r0  */
+/* { dg-do compile { target "sh*-*-*" } } */
+/* { dg-options "-O2" } */
+/* { dg-skip-if "" { "sh*-*-*" } { "*"} { "-m3* -m2a* -m4*" } }  */
+/* { dg-final { scan-assembler-not "neg" } } */
+
+unsigned int
+test (unsigned int a, int b, int c)
+{
+  unsigned char r = b == c;
+  return ((a >> 31) | (r << 31));
+}

Reply via email to