diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b4d23116fd42b7bd833ba53ab347011fcee31168..acdf3d61c579e0e58551ca2f41dc0d495f14e915 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,11 @@
+2012-09-19  Oleg Endo  <olegendo@gcc.gnu.org>
+
+	PR target/54089
+	* config/sh/predicates.md (arith_reg_or_t_reg_operand): New predicate.
+	* config/sh/sh.md (*rotcr): Use arith_reg_or_t_reg_operand predicate.
+	Handle the case where one of the operands is T_REG.
+	Add new pattern to handle MSB extraction.
+
 2012-09-19  Oleg Endo  <olegendo@gcc.gnu.org>
 
 	PR target/54236
diff --git a/gcc/config/sh/predicates.md b/gcc/config/sh/predicates.md
index 8a4f7db044cdfb375f8f942261793ee11b5d6446..27e42f159bc169584200bf1d565f27266b5f710c 100644
--- a/gcc/config/sh/predicates.md
+++ b/gcc/config/sh/predicates.md
@@ -1028,3 +1028,8 @@
 	return 0;
     }
 })
+
+;; Returns true of OP is arith_reg_operand or t_reg_operand.
+(define_predicate "arith_reg_or_t_reg_operand"
+  (ior (match_operand 0 "arith_reg_operand")
+       (match_operand 0 "t_reg_operand")))
diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md
index 71e758b3aee5dcd147e00209a2c12531916a4087..27c5633dd9dad601057dad05d6ff02f15966935d 100644
--- a/gcc/config/sh/sh.md
+++ b/gcc/config/sh/sh.md
@@ -3940,7 +3940,7 @@ label:
   [(set (match_operand:SI 0 "arith_reg_dest")
 	(ior:SI (lshiftrt:SI (match_operand:SI 1 "arith_reg_operand")
 			     (match_operand:SI 2 "const_int_operand"))
-		(ashift:SI (match_operand:SI 3 "t_reg_operand")
+		(ashift:SI (match_operand:SI 3 "arith_reg_or_t_reg_operand")
 			   (const_int 31))))
    (clobber (reg:SI T_REG))]
   "TARGET_SH1"
@@ -3992,6 +3992,17 @@ label:
 	emit_insn (gen_cmpgtsi_t (tmp_t_reg, const0_rtx));
     }
 
+  /* For the rotcr insn to work, operands[3] must be in T_REG.
+     If it is not we can get it there by shifting it right one bit.
+     In this case T_REG is not an input for this insn, thus we don't have to
+     pay attention as of where to insert the shlr insn.  */
+  if (! t_reg_operand (operands[3], SImode))
+    {
+      /* We don't care about the shifted result here, only the T_REG.  */
+      emit_insn (gen_shlr (gen_reg_rtx (SImode), operands[3]));
+      operands[3] = get_t_reg_rtx ();
+    }
+
   emit_insn (gen_rotcr (operands[0], operands[1], operands[3]));
   DONE;
 })
@@ -4011,6 +4022,24 @@ label:
 	      (set (reg:SI T_REG)
 		   (and:SI (match_dup 0) (const_int 1)))])])
 
+(define_insn_and_split "*rotcr"
+  [(set (match_operand:SI 0 "arith_reg_dest")
+	(ior:SI (and:SI (match_operand:SI 1 "arith_reg_operand")
+			(const_int -2147483648)) ;; 0xffffffff80000000
+		(lshiftrt:SI (match_operand:SI 2 "arith_reg_operand")
+			     (const_int 1))))
+   (clobber (reg:SI T_REG))]
+  "TARGET_SH1"
+  "#"
+  "&& can_create_pseudo_p ()"
+  [(const_int 0)]
+{
+  rtx tmp = gen_reg_rtx (SImode);
+  emit_insn (gen_shll (tmp, operands[1]));
+  emit_insn (gen_rotcr (operands[0], operands[2], get_t_reg_rtx ()));
+  DONE;
+})
+
 ;; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
 ;; SImode shift left
 
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index c4c041f84930d075d1600d6ec072d2bee5a44085..411796655a95b5774f2bf940504a72ea9ba5fe9d 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2012-09-19  Oleg Endo  <olegendo@gcc.gnu.org>
+
+	PR target/54089
+	* gcc.target/sh/pr54089-1.c (test_11, test_12, test_13, test_14): New
+	functions.
+
 2012-09-19  Oleg Endo  <olegendo@gcc.gnu.org>
 
 	PR target/54236
diff --git a/gcc/testsuite/gcc.target/sh/pr54089-1.c b/gcc/testsuite/gcc.target/sh/pr54089-1.c
index 2101c53585a00d348114addd6ad1436bf3b787de..77924554f38e0c60b1a716f8444add09e791a541 100644
--- a/gcc/testsuite/gcc.target/sh/pr54089-1.c
+++ b/gcc/testsuite/gcc.target/sh/pr54089-1.c
@@ -2,7 +2,8 @@
 /* { dg-do compile { target "sh*-*-*" } } */
 /* { dg-options "-O1" } */
 /* { dg-skip-if "" { "sh*-*-*" } { "-m5*"} { "" } }  */
-/* { dg-final { scan-assembler-times "rotcr" 11 } } */
+/* { dg-final { scan-assembler-times "rotcr" 15 } } */
+/* { dg-final { scan-assembler-times "shll\t" 1 } } */
 
 typedef char bool;
 
@@ -81,3 +82,30 @@ test_10 (int a, int b)
   bool r = a == b;
   return r << 31;
 }
+
+unsigned int
+test_11 (unsigned int a, int b)
+{
+  /* 1x shlr, 1x rotcr  */
+  return (a >> 1) | (b << 31);
+}
+
+unsigned int
+test_12 (unsigned int a, int b)
+{
+  return (a >> 2) | (b << 31);
+}
+
+unsigned int
+test_13 (unsigned int a, int b)
+{
+  return (a >> 3) | (b << 31);
+}
+
+unsigned int
+test_14 (unsigned int a, int b)
+{
+  /* 1x shll, 1x rotcr  */
+  bool r = b < 0;
+  return ((a >> 1) | (r << 31));
+}