From 0aa222d17b32f6d6388e1e92a1d7bde124277f3d Mon Sep 17 00:00:00 2001
From: Sandra Loosemore <sandra@codesourcery.com>
Date: Thu, 5 Jul 2007 13:08:37 -0400
Subject: [PATCH] Add support for SmartMIPS ASE.

2007-07-05  Sandra Loosemore  <sandra@codesourcery.com>
	    David Ung  <davidu@mips.com>

	Add support for SmartMIPS ASE.

	gcc/
	* optabs.c (expand_binop_directly): New, broken out from...
	(expand_binop): Here.  Make it try rotating in the other
	direction even when the second operand isn't constant.
	* config/mips/mips.md (*lwxs): New.
	* config/mips/mips.opt (msmartmips): New.
	* config/mips/mips.c (mips_lwxs_address_p): New.
	(mips_rtx_costs): Make it recognize scaled indexed addressing.
	* config/mips/mips.h (TARGET_CPU_CPP_BUILTINS): Define
	__mips_smartmips when compiling for TARGET_SMARTMIPS.
	(ISA_HAS_ROR): Define for TARGET_SMARTMIPS.
	(ISA_HAS_LWXS): New.
	(ASM_SPEC): Add -msmartmips/-mno-smartmips.
	* doc/invoke.texi (MIPS Options): Document -msmartmips/-mno-smartmips.
	* testsuite/gcc.target/mips/smartmips-lwxs.c: New test case.
	* testsuite/gcc.target/mips/smartmips-ror-1.c: New test case.
	* testsuite/gcc.target/mips/smartmips-ror-2.c: New test case.
	* testsuite/gcc.target/mips/smartmips-ror-3.c: New test case.
	* testsuite/gcc.target/mips/smartmips-ror-4.c: New test case.

Co-Authored-By: David Ung <davidu@mips.com>

From-SVN: r126370
---
 gcc/ChangeLog                                 |  24 ++
 gcc/config/mips/mips.c                        |  32 ++-
 gcc/config/mips/mips.h                        |  10 +-
 gcc/config/mips/mips.md                       |  15 ++
 gcc/config/mips/mips.opt                      |   4 +
 gcc/doc/invoke.texi                           |   7 +
 gcc/optabs.c                                  | 246 ++++++++++--------
 .../gcc.target/mips/smartmips-lwxs.c          |   8 +
 .../gcc.target/mips/smartmips-ror-1.c         |   8 +
 .../gcc.target/mips/smartmips-ror-2.c         |   8 +
 .../gcc.target/mips/smartmips-ror-3.c         |  10 +
 .../gcc.target/mips/smartmips-ror-4.c         |  10 +
 12 files changed, 269 insertions(+), 113 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/mips/smartmips-lwxs.c
 create mode 100644 gcc/testsuite/gcc.target/mips/smartmips-ror-1.c
 create mode 100644 gcc/testsuite/gcc.target/mips/smartmips-ror-2.c
 create mode 100644 gcc/testsuite/gcc.target/mips/smartmips-ror-3.c
 create mode 100644 gcc/testsuite/gcc.target/mips/smartmips-ror-4.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index e39fb1431197..2fecaf1c0cbd 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,27 @@
+2007-07-05  Sandra Loosemore  <sandra@codesourcery.com>
+	    David Ung  <davidu@mips.com>
+
+	Add support for SmartMIPS ASE.
+
+	* optabs.c (expand_binop_directly): New, broken out from...
+	(expand_binop): Here.  Make it try rotating in the other
+	direction even when the second operand isn't constant.
+	* config/mips/mips.md (*lwxs): New.
+	* config/mips/mips.opt (msmartmips): New.
+	* config/mips/mips.c (mips_lwxs_address_p): New.
+	(mips_rtx_costs): Make it recognize scaled indexed addressing.
+	* config/mips/mips.h (TARGET_CPU_CPP_BUILTINS): Define
+	__mips_smartmips when compiling for TARGET_SMARTMIPS.
+	(ISA_HAS_ROR): Define for TARGET_SMARTMIPS.
+	(ISA_HAS_LWXS): New.
+	(ASM_SPEC): Add -msmartmips/-mno-smartmips.
+	* doc/invoke.texi (MIPS Options): Document -msmartmips/-mno-smartmips.
+	* testsuite/gcc.target/mips/smartmips-lwxs.c: New test case.
+	* testsuite/gcc.target/mips/smartmips-ror-1.c: New test case.
+	* testsuite/gcc.target/mips/smartmips-ror-2.c: New test case.
+	* testsuite/gcc.target/mips/smartmips-ror-3.c: New test case.
+	* testsuite/gcc.target/mips/smartmips-ror-4.c: New test case.
+
 2007-07-05  Dorit Nuzman  <dorit@il.ibm.com>
 
 	* tree-vectorizer.c (new_loop_vec_info): Initialize
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index da93ddcd36f0..7b101e32555b 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -2682,6 +2682,26 @@ m16_nsimm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
   return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7);
 }
 
+/* Return true if ADDR matches the pattern for the lwxs load scaled indexed
+   address instruction.  */
+
+static bool
+mips_lwxs_address_p (rtx addr)
+{
+  if (ISA_HAS_LWXS
+      && GET_CODE (addr) == PLUS
+      && REG_P (XEXP (addr, 1)))
+    {
+      rtx offset = XEXP (addr, 0);
+      if (GET_CODE (offset) == MULT
+	  && REG_P (XEXP (offset, 0))
+	  && GET_CODE (XEXP (offset, 1)) == CONST_INT
+	  && INTVAL (XEXP (offset, 1)) == 4)
+	return true;
+    }
+  return false;
+}
+
 static bool
 mips_rtx_costs (rtx x, int code, int outer_code, int *total)
 {
@@ -2778,13 +2798,21 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
     case MEM:
       {
 	/* If the address is legitimate, return the number of
-	   instructions it needs, otherwise use the default handling.  */
-	int n = mips_address_insns (XEXP (x, 0), GET_MODE (x));
+	   instructions it needs.  */
+	rtx addr = XEXP (x, 0);
+	int n = mips_address_insns (addr, GET_MODE (x));
 	if (n > 0)
 	  {
 	    *total = COSTS_N_INSNS (n + 1);
 	    return true;
 	  }
+	/* Check for scaled indexed address.  */
+	if (mips_lwxs_address_p (addr))
+	  {
+	    *total = COSTS_N_INSNS (2);
+	    return true;
+	  }
+	/* Otherwise use the default handling.  */
 	return false;
       }
 
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index 8338829e9418..2425d13ab1ee 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -366,6 +366,9 @@ extern const struct mips_rtx_cost_data *mips_cost;
 								\
       if (TARGET_MIPS3D)					\
 	builtin_define ("__mips3d");				\
+                                                                \
+      if (TARGET_SMARTMIPS)					\
+	builtin_define ("__mips_smartmips");			\
 								\
       if (TARGET_DSP)						\
 	builtin_define ("__mips_dsp");				\
@@ -733,7 +736,8 @@ extern const struct mips_rtx_cost_data *mips_cost;
 #define ISA_HAS_ROR		((ISA_MIPS32R2				\
 				  || TARGET_MIPS5400			\
 				  || TARGET_MIPS5500			\
-				  || TARGET_SR71K)			\
+				  || TARGET_SR71K			\
+				  || TARGET_SMARTMIPS)			\
 				 && !TARGET_MIPS16)
 
 /* ISA has data prefetch instructions.  This controls use of 'pref'.  */
@@ -768,6 +772,9 @@ extern const struct mips_rtx_cost_data *mips_cost;
 /* ISA has instructions for accessing top part of 64-bit fp regs.  */
 #define ISA_HAS_MXHC1		(TARGET_FLOAT64 && ISA_MIPS32R2)
 
+/* ISA has lwxs instruction (load w/scaled index address.  */
+#define ISA_HAS_LWXS		(TARGET_SMARTMIPS && !TARGET_MIPS16)
+
 /* True if the result of a load is not available to the next instruction.
    A nop will then be needed between instructions like "lw $4,..."
    and "addiu $4,$4,1".  */
@@ -883,6 +890,7 @@ extern const struct mips_rtx_cost_data *mips_cost;
 %{mdmx} %{mno-mdmx:-no-mdmx} \
 %{mdsp} %{mno-dsp} \
 %{mdspr2} %{mno-dspr2} \
+%{msmartmips} %{mno-smartmips} \
 %{mmt} %{mno-mt} \
 %{mfix-vr4120} %{mfix-vr4130} \
 %(subtarget_asm_optimizing_spec) \
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index 905b365dfa7b..2b7a2d2368f8 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -3654,6 +3654,21 @@
   [(set_attr "type" "fpidxstore")
    (set_attr "mode" "<ANYF:UNITMODE>")])
 
+;; Scaled indexed address load.
+;; Per md.texi, we only need to look for a pattern with multiply in the
+;; address expression, not shift.
+
+(define_insn "*lwxs"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+	(mem:SI (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d")
+				  (const_int 4))
+			 (match_operand:SI 2 "register_operand" "d"))))]
+  "ISA_HAS_LWXS"
+  "lwxs\t%0,%1(%2)"
+  [(set_attr "type"	"load")
+   (set_attr "mode"	"SI")
+   (set_attr "length"   "4")])
+
 ;; 16-bit Integer moves
 
 ;; Unlike most other insns, the move insns can't be split with
diff --git a/gcc/config/mips/mips.opt b/gcc/config/mips/mips.opt
index f0c2dbf4c336..f7e751fb7132 100644
--- a/gcc/config/mips/mips.opt
+++ b/gcc/config/mips/mips.opt
@@ -209,6 +209,10 @@ msingle-float
 Target Report RejectNegative Mask(SINGLE_FLOAT)
 Restrict the use of hardware floating-point instructions to 32-bit operations
 
+msmartmips
+Target Report RejectNegative Mask(SMARTMIPS)
+Use SmartMIPS instructions
+
 msoft-float
 Target Report RejectNegative Mask(SOFT_FLOAT)
 Prevent the use of all hardware floating-point instructions
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b1bbcf0bb02f..f7523c28b3ab 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -622,6 +622,7 @@ Objective-C and Objective-C++ Dialects}.
 -mshared  -mno-shared  -mxgot  -mno-xgot  -mgp32  -mgp64 @gol
 -mfp32  -mfp64  -mhard-float  -msoft-float @gol
 -msingle-float  -mdouble-float  -mdsp  -mno-dsp  -mdspr2  -mno-dspr2 @gol
+-msmartmips  -mno-smartmips @gol
 -mpaired-single  -mno-paired-single  -mdmx  -mno-mdmx @gol
 -mips3d  -mno-mips3d  -mmt  -mno-mt @gol
 -mlong64  -mlong32  -msym32  -mno-sym32 @gol
@@ -11662,6 +11663,12 @@ Use (do not use) the MIPS DSP ASE.  @xref{MIPS DSP Built-in Functions}.
 Use (do not use) the MIPS DSP ASE REV 2.  @xref{MIPS DSP Built-in Functions}.
 The option @option{-mdspr2} implies @option{-mdsp}.
 
+@item -msmartmips
+@itemx -mno-smartmips
+@opindex msmartmips
+@opindex mno-smartmips
+Use (do not use) the MIPS SmartMIPS ASE.
+
 @item -mpaired-single
 @itemx -mno-paired-single
 @opindex mpaired-single
diff --git a/gcc/optabs.c b/gcc/optabs.c
index dd146da8b6d3..c07cc06abd6d 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -1246,6 +1246,113 @@ swap_commutative_operands_with_target (rtx target, rtx op0, rtx op1)
 }
 
 
+/* Helper function for expand_binop: handle the case where there
+   is an insn that directly implements the indicated operation.
+   Returns null if this is not possible.  */
+static rtx
+expand_binop_directly (enum machine_mode mode, optab binoptab,
+		       rtx op0, rtx op1,
+		       rtx target, int unsignedp, enum optab_methods methods,
+		       int commutative_op, rtx last)
+{
+  int icode = (int) binoptab->handlers[(int) mode].insn_code;
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+  enum machine_mode tmp_mode;
+  rtx pat;
+  rtx xop0 = op0, xop1 = op1;
+  rtx temp;
+  
+  if (target)
+    temp = target;
+  else
+    temp = gen_reg_rtx (mode);
+  
+  /* If it is a commutative operator and the modes would match
+     if we would swap the operands, we can save the conversions.  */
+  if (commutative_op)
+    {
+      if (GET_MODE (op0) != mode0 && GET_MODE (op1) != mode1
+	  && GET_MODE (op0) == mode1 && GET_MODE (op1) == mode0)
+	{
+	  rtx tmp;
+	  
+	  tmp = op0; op0 = op1; op1 = tmp;
+	  tmp = xop0; xop0 = xop1; xop1 = tmp;
+	}
+    }
+  
+  /* In case the insn wants input operands in modes different from
+     those of the actual operands, convert the operands.  It would
+     seem that we don't need to convert CONST_INTs, but we do, so
+     that they're properly zero-extended, sign-extended or truncated
+     for their mode.  */
+  
+  if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
+    xop0 = convert_modes (mode0,
+			  GET_MODE (op0) != VOIDmode
+			  ? GET_MODE (op0)
+			  : mode,
+			  xop0, unsignedp);
+  
+  if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
+    xop1 = convert_modes (mode1,
+			  GET_MODE (op1) != VOIDmode
+			  ? GET_MODE (op1)
+			  : mode,
+			  xop1, unsignedp);
+  
+  /* Now, if insn's predicates don't allow our operands, put them into
+     pseudo regs.  */
+  
+  if (!insn_data[icode].operand[1].predicate (xop0, mode0)
+      && mode0 != VOIDmode)
+    xop0 = copy_to_mode_reg (mode0, xop0);
+  
+  if (!insn_data[icode].operand[2].predicate (xop1, mode1)
+      && mode1 != VOIDmode)
+    xop1 = copy_to_mode_reg (mode1, xop1);
+  
+  if (binoptab == vec_pack_trunc_optab 
+      || binoptab == vec_pack_usat_optab
+      || binoptab == vec_pack_ssat_optab
+      || binoptab == vec_pack_ufix_trunc_optab
+      || binoptab == vec_pack_sfix_trunc_optab)
+    {
+      /* The mode of the result is different then the mode of the
+	 arguments.  */
+      tmp_mode = insn_data[icode].operand[0].mode;
+      if (GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode))
+	return 0;
+    }
+  else
+    tmp_mode = mode;
+
+  if (!insn_data[icode].operand[0].predicate (temp, tmp_mode))
+    temp = gen_reg_rtx (tmp_mode);
+  
+  pat = GEN_FCN (icode) (temp, xop0, xop1);
+  if (pat)
+    {
+      /* If PAT is composed of more than one insn, try to add an appropriate
+	 REG_EQUAL note to it.  If we can't because TEMP conflicts with an
+	 operand, call expand_binop again, this time without a target.  */
+      if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
+	  && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1))
+	{
+	  delete_insns_since (last);
+	  return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
+			       unsignedp, methods);
+	}
+      
+      emit_insn (pat);
+      return temp;
+    }
+
+  delete_insns_since (last);
+  return NULL_RTX;
+}
+
 /* Generate code to perform an operation specified by BINOPTAB
    on operands OP0 and OP1, with result having machine-mode MODE.
 
@@ -1275,7 +1382,6 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 		  || binoptab->code == ROTATERT);
   rtx entry_last = get_last_insn ();
   rtx last;
-  bool first_pass_p = true;
 
   class = GET_MODE_CLASS (mode);
 
@@ -1329,123 +1435,43 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1,
 	}
     }
 
- retry:
-
   /* If we can do it with a three-operand insn, do so.  */
 
   if (methods != OPTAB_MUST_WIDEN
       && binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
     {
-      int icode = (int) binoptab->handlers[(int) mode].insn_code;
-      enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-      enum machine_mode mode1 = insn_data[icode].operand[2].mode;
-      enum machine_mode tmp_mode;
-      rtx pat;
-      rtx xop0 = op0, xop1 = op1;
-
-      if (target)
-	temp = target;
-      else
-	temp = gen_reg_rtx (mode);
-
-      /* If it is a commutative operator and the modes would match
-	 if we would swap the operands, we can save the conversions.  */
-      if (commutative_op)
-	{
-	  if (GET_MODE (op0) != mode0 && GET_MODE (op1) != mode1
-	      && GET_MODE (op0) == mode1 && GET_MODE (op1) == mode0)
-	    {
-	      rtx tmp;
-
-	      tmp = op0; op0 = op1; op1 = tmp;
-	      tmp = xop0; xop0 = xop1; xop1 = tmp;
-	    }
-	}
-
-      /* In case the insn wants input operands in modes different from
-	 those of the actual operands, convert the operands.  It would
-	 seem that we don't need to convert CONST_INTs, but we do, so
-	 that they're properly zero-extended, sign-extended or truncated
-	 for their mode.  */
-
-      if (GET_MODE (op0) != mode0 && mode0 != VOIDmode)
-	xop0 = convert_modes (mode0,
-			      GET_MODE (op0) != VOIDmode
-			      ? GET_MODE (op0)
-			      : mode,
-			      xop0, unsignedp);
-
-      if (GET_MODE (op1) != mode1 && mode1 != VOIDmode)
-	xop1 = convert_modes (mode1,
-			      GET_MODE (op1) != VOIDmode
-			      ? GET_MODE (op1)
-			      : mode,
-			      xop1, unsignedp);
-
-      /* Now, if insn's predicates don't allow our operands, put them into
-	 pseudo regs.  */
-
-      if (!insn_data[icode].operand[1].predicate (xop0, mode0)
-	  && mode0 != VOIDmode)
-	xop0 = copy_to_mode_reg (mode0, xop0);
-
-      if (!insn_data[icode].operand[2].predicate (xop1, mode1)
-	  && mode1 != VOIDmode)
-	xop1 = copy_to_mode_reg (mode1, xop1);
-
-      if (binoptab == vec_pack_trunc_optab 
-	  || binoptab == vec_pack_usat_optab
-	  || binoptab == vec_pack_ssat_optab
-	  || binoptab == vec_pack_ufix_trunc_optab
-	  || binoptab == vec_pack_sfix_trunc_optab)
-	{
-	  /* The mode of the result is different then the mode of the
-	     arguments.  */
-	  tmp_mode = insn_data[icode].operand[0].mode;
-	  if (GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode))
-	    return 0;
-	}
-      else
-        tmp_mode = mode;
-
-      if (!insn_data[icode].operand[0].predicate (temp, tmp_mode))
-	temp = gen_reg_rtx (tmp_mode);
-
-      pat = GEN_FCN (icode) (temp, xop0, xop1);
-      if (pat)
-	{
-	  /* If PAT is composed of more than one insn, try to add an appropriate
-	     REG_EQUAL note to it.  If we can't because TEMP conflicts with an
-	     operand, call ourselves again, this time without a target.  */
-	  if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX
-	      && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1))
-	    {
-	      delete_insns_since (last);
-	      return expand_binop (mode, binoptab, op0, op1, NULL_RTX,
-				   unsignedp, methods);
-	    }
-
-	  emit_insn (pat);
-	  return temp;
-	}
-      else
-	delete_insns_since (last);
+      temp = expand_binop_directly (mode, binoptab, op0, op1, target,
+				    unsignedp, methods, commutative_op, last);
+      if (temp)
+	return temp;
     }
 
-  /* If we were trying to rotate by a constant value, and that didn't
-     work, try rotating the other direction before falling back to
-     shifts and bitwise-or.  */
-  if (first_pass_p
-      && (binoptab == rotl_optab || binoptab == rotr_optab)
-      && class == MODE_INT
-      && GET_CODE (op1) == CONST_INT
-      && INTVAL (op1) > 0
-      && (unsigned int) INTVAL (op1) < GET_MODE_BITSIZE (mode))
+  /* If we were trying to rotate, and that didn't work, try rotating
+     the other direction before falling back to shifts and bitwise-or.  */
+  if (((binoptab == rotl_optab
+	&& rotr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
+       || (binoptab == rotr_optab
+	   && rotl_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing))
+      && class == MODE_INT)
     {
-      first_pass_p = false;
-      op1 = GEN_INT (GET_MODE_BITSIZE (mode) - INTVAL (op1));
-      binoptab = binoptab == rotl_optab ? rotr_optab : rotl_optab;
-      goto retry;
+      optab otheroptab = (binoptab == rotl_optab ? rotr_optab : rotl_optab);
+      rtx newop1;
+      int bits = GET_MODE_BITSIZE (mode);
+
+      if (GET_CODE (op1) == CONST_INT)
+	newop1 = GEN_INT (bits - INTVAL (op1));
+      else if (targetm.shift_truncation_mask (mode) == bits - 1)
+	newop1 = negate_rtx (mode, op1);
+      else
+	newop1 = expand_binop (mode, sub_optab,
+			       GEN_INT (bits), op1,
+			       NULL_RTX, unsignedp, OPTAB_DIRECT);
+				   
+      temp = expand_binop_directly (mode, otheroptab, op0, newop1,
+				    target, unsignedp, methods,
+				    commutative_op, last);
+      if (temp)
+	return temp;
     }
 
   /* If this is a multiply, see if we can do a widening operation that
diff --git a/gcc/testsuite/gcc.target/mips/smartmips-lwxs.c b/gcc/testsuite/gcc.target/mips/smartmips-lwxs.c
new file mode 100644
index 000000000000..cd9b0b3950f3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/smartmips-lwxs.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-mips-options "-O -msmartmips -mno-mips16" } */
+
+int scaled_indexed_word_load (int a[], int b)
+{
+  return a[b];
+}
+/* { dg-final { scan-assembler "\tlwxs\t" } } */
diff --git a/gcc/testsuite/gcc.target/mips/smartmips-ror-1.c b/gcc/testsuite/gcc.target/mips/smartmips-ror-1.c
new file mode 100644
index 000000000000..5ad7f3424c89
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/smartmips-ror-1.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-mips-options "-O -msmartmips -mno-mips16" } */
+
+int rotate_left (unsigned a, unsigned s)
+{
+  return (a << s) | (a >> (32 - s));
+}
+/* { dg-final { scan-assembler "\tror\t" } } */
diff --git a/gcc/testsuite/gcc.target/mips/smartmips-ror-2.c b/gcc/testsuite/gcc.target/mips/smartmips-ror-2.c
new file mode 100644
index 000000000000..93d376d537d7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/smartmips-ror-2.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-mips-options "-O -msmartmips -mno-mips16" } */
+
+int rotate_right (unsigned a, unsigned s)
+{
+  return (a >> s) | (a << (32 - s));
+}
+/* { dg-final { scan-assembler "\tror\t" } } */
diff --git a/gcc/testsuite/gcc.target/mips/smartmips-ror-3.c b/gcc/testsuite/gcc.target/mips/smartmips-ror-3.c
new file mode 100644
index 000000000000..ec1c6e27e59d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/smartmips-ror-3.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-mips-options "-O -msmartmips -mno-mips16" } */
+
+#define S 13
+
+int rotate_left_constant (unsigned a)
+{
+  return (a << S) | (a >> (32 - S));
+}
+/* { dg-final { scan-assembler "\tror\t" } } */
diff --git a/gcc/testsuite/gcc.target/mips/smartmips-ror-4.c b/gcc/testsuite/gcc.target/mips/smartmips-ror-4.c
new file mode 100644
index 000000000000..2a56210539f0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/mips/smartmips-ror-4.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-mips-options "-O -msmartmips -mno-mips16" } */
+
+#define S 13
+
+int rotate_right_constant (unsigned a)
+{
+  return (a >> S) | (a << (32 - S));
+}
+/* { dg-final { scan-assembler "\tror\t" } } */
-- 
GitLab