diff --git a/gcc/config/rs6000/rs6000-builtins.def b/gcc/config/rs6000/rs6000-builtins.def
index 289a37998b1..30e0b0bb06d 100644
--- a/gcc/config/rs6000/rs6000-builtins.def
+++ b/gcc/config/rs6000/rs6000-builtins.def
@@ -237,8 +237,11 @@
   const __ibm128 __builtin_pack_ibm128 (double, double);
     PACK_IF packif {ibm128}
 
-  void __builtin_set_fpscr_rn (const int[0,3]);
-    SET_FPSCR_RN rs6000_set_fpscr_rn {nosoft}
+  double __builtin_set_fpscr_rn_i (const int[0,3]);
+    SET_FPSCR_RN_I rs6000_set_fpscr_rn_i {nosoft}
+
+  double __builtin_set_fpscr_rn_d (double);
+    SET_FPSCR_RN_D rs6000_set_fpscr_rn_d {nosoft}
 
   const double __builtin_unpack_ibm128 (__ibm128, const int<1>);
     UNPACK_IF unpackif {ibm128}
diff --git a/gcc/config/rs6000/rs6000-overload.def b/gcc/config/rs6000/rs6000-overload.def
index c582490c084..bb904b85820 100644
--- a/gcc/config/rs6000/rs6000-overload.def
+++ b/gcc/config/rs6000/rs6000-overload.def
@@ -195,6 +195,12 @@
   unsigned long long __builtin_cmpb (unsigned long long, unsigned long long);
     CMPB
 
+[SET_FPSCR_RN, set_fpscr_rn, __builtin_set_fpscr_rn]
+  double __builtin_set_fpscr_rn (int);
+    SET_FPSCR_RN_I
+  double __builtin_set_fpscr_rn (double);
+    SET_FPSCR_RN_D
+
 [VEC_ABS, vec_abs, __builtin_vec_abs]
   vsc __builtin_vec_abs (vsc);
     ABS_V16QI
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index b0db8ae508d..5c53383c879 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -6440,8 +6440,84 @@
   "mffscdrn %0,%1"
   [(set_attr "type" "fp")])
 
-(define_expand "rs6000_set_fpscr_rn"
- [(match_operand:SI 0 "reg_or_cint_operand")]
+
+(define_expand "rs6000_get_fpscr_fields"
+ [(match_operand:DF 0 "gpc_reg_operand")]
+  "TARGET_HARD_FLOAT"
+{
+  /* Extract fields bits 29:31 (DRN) and bits 56:63 (VE, OE, UE, ZE, XE, NI,
+     RN) from the FPSCR and return them.  */
+  rtx tmp_df = gen_reg_rtx (DFmode);
+  rtx tmp_di = gen_reg_rtx (DImode);
+
+  emit_insn (gen_rs6000_mffs (tmp_df));
+  tmp_di = simplify_gen_subreg (DImode, tmp_df, DFmode, 0);
+  emit_insn (gen_anddi3 (tmp_di, tmp_di, GEN_INT (0x00000007000000FFULL)));
+  rtx tmp_rtn = simplify_gen_subreg (DFmode, tmp_di, DImode, 0);
+  emit_move_insn (operands[0], tmp_rtn);
+  DONE;
+})
+
+(define_expand "rs6000_update_fpscr_rn_field"
+ [(match_operand:DI 0 "gpc_reg_operand")]
+  "TARGET_HARD_FLOAT"
+{
+  /* Insert the new RN value from operands[0] into FPSCR bit [62:63].  */
+  rtx tmp_di = gen_reg_rtx (DImode);
+  rtx tmp_df = gen_reg_rtx (DFmode);
+
+  emit_insn (gen_rs6000_mffs (tmp_df));
+  tmp_di = simplify_gen_subreg (DImode, tmp_df, DFmode, 0);
+  emit_insn (gen_anddi3 (tmp_di, tmp_di, GEN_INT (-4)));
+  emit_insn (gen_iordi3 (tmp_di, tmp_di, operands[0]));
+
+  /* Need to write to field k=15.  The fields are [0:15].  Hence with
+     L=0, W=0, FLM_i must be equal to 8, 16 = i + 8*(1-W).  FLM is an
+     8-bit field[0:7]. Need to set the bit that corresponds to the
+     value of i that you want [0:7].  */
+
+  tmp_df = simplify_gen_subreg (DFmode, tmp_di, DImode, 0);
+  emit_insn (gen_rs6000_mtfsf (GEN_INT (0x01), tmp_df));
+  DONE;
+})
+
+(define_expand "rs6000_set_fpscr_rn_d"
+ [(set (match_operand:DF 0 "gpc_reg_operand")
+       (unspec_volatile:DF [(match_operand:DF 1 "gpc_reg_operand")]
+       UNSPECV_MFFSCDRN))]
+  "TARGET_HARD_FLOAT"
+{
+  rtx tmp_df = gen_reg_rtx (DFmode);
+
+  if (TARGET_P9_MISC)
+    {
+       emit_insn (gen_rs6000_mffscrn (tmp_df, operands[1]));
+       emit_move_insn (operands[0], tmp_df);
+       DONE;
+    }
+
+  /* Emulate the behavior of the mffscrni, mffscrn instructions for earlier
+     ISAs.  Return bits 29:31 (DRN) and bits 56:63 (VE, OE, UE, ZE, XE, NI,
+     RN) from the FPSCR.  Set the RN field based on the value in operands[1].
+  */
+
+  /* Get the return value */
+  emit_insn (gen_rs6000_get_fpscr_fields (operands[0]));
+
+  /* Extract new RN mode from operand.  */
+  rtx op1 = simplify_gen_subreg (DImode, operands[1], DFmode, 0);
+  rtx tmp_rn = gen_reg_rtx (DImode);
+  emit_insn (gen_anddi3 (tmp_rn, op1, GEN_INT (3)));
+
+  /* Insert new RN mode into FSCPR.  */
+  emit_insn (gen_rs6000_update_fpscr_rn_field (tmp_rn));
+  DONE;
+})
+
+(define_expand "rs6000_set_fpscr_rn_i"
+ [(set (match_operand:DF 0 "gpc_reg_operand")
+       (unspec_volatile:DF [(match_operand:SI 1 "reg_or_cint_operand")]
+       UNSPECV_MFFSCDRN))]
   "TARGET_HARD_FLOAT"
 {
   rtx tmp_df = gen_reg_rtx (DFmode);
@@ -6450,25 +6526,34 @@
      new rounding mode bits from operands[0][62:63] into FPSCR[62:63].  */
   if (TARGET_P9_MISC)
     {
-      if (const_0_to_3_operand (operands[0], VOIDmode))
-	emit_insn (gen_rs6000_mffscrni (tmp_df, operands[0]));
+      if (const_0_to_3_operand (operands[1], VOIDmode))
+	emit_insn (gen_rs6000_mffscrni (tmp_df, operands[1]));
       else
 	{
-	  rtx op0 = convert_to_mode (DImode, operands[0], false);
-	  rtx src_df = simplify_gen_subreg (DFmode, op0, DImode, 0);
+	  rtx op1 = convert_to_mode (DImode, operands[1], false);
+	  rtx src_df = simplify_gen_subreg (DFmode, op1, DImode, 0);
 	  emit_insn (gen_rs6000_mffscrn (tmp_df, src_df));
 	}
-      DONE;
+       emit_move_insn (operands[0], tmp_df);
+       DONE;
     }
 
-  if (CONST_INT_P (operands[0]))
+  /* Emulate the behavior of the mffscrni, mffscrn instructions for earlier
+     ISAs.  Return bits 29:31 (DRN) and bits 56:63 (VE, OE, UE, ZE, XE, NI,
+     RN) from the FPSCR.  Set the RN field based on the value in operands[1].
+  */
+
+  /* Get the current FPSCR fields to return.  */
+  emit_insn (gen_rs6000_get_fpscr_fields (operands[0]));
+
+  if (CONST_INT_P (operands[1]))
     {
-      if ((INTVAL (operands[0]) & 0x1) == 0x1)
+      if ((INTVAL (operands[1]) & 0x1) == 0x1)
 	emit_insn (gen_rs6000_mtfsb1 (GEN_INT (31)));
       else
 	emit_insn (gen_rs6000_mtfsb0 (GEN_INT (31)));
 
-      if ((INTVAL (operands[0]) & 0x2) == 0x2)
+      if ((INTVAL (operands[1]) & 0x2) == 0x2)
 	emit_insn (gen_rs6000_mtfsb1 (GEN_INT (30)));
       else
 	emit_insn (gen_rs6000_mtfsb0 (GEN_INT (30)));
@@ -6476,24 +6561,13 @@
   else
     {
       rtx tmp_rn = gen_reg_rtx (DImode);
-      rtx tmp_di = gen_reg_rtx (DImode);
 
       /* Extract new RN mode from operand.  */
-      rtx op0 = convert_to_mode (DImode, operands[0], false);
-      emit_insn (gen_anddi3 (tmp_rn, op0, GEN_INT (3)));
+      rtx op1 = convert_to_mode (DImode, operands[1], false);
+      emit_insn (gen_anddi3 (tmp_rn, op1, GEN_INT (3)));
 
       /* Insert new RN mode into FSCPR.  */
-      emit_insn (gen_rs6000_mffs (tmp_df));
-      tmp_di = simplify_gen_subreg (DImode, tmp_df, DFmode, 0);
-      emit_insn (gen_anddi3 (tmp_di, tmp_di, GEN_INT (-4)));
-      emit_insn (gen_iordi3 (tmp_di, tmp_di, tmp_rn));
-
-      /* Need to write to field k=15.  The fields are [0:15].  Hence with
-	 L=0, W=0, FLM_i must be equal to 8, 16 = i + 8*(1-W).  FLM is an
-	 8-bit field[0:7]. Need to set the bit that corresponds to the
-	 value of i that you want [0:7].  */
-      tmp_df = simplify_gen_subreg (DFmode, tmp_di, DImode, 0);
-      emit_insn (gen_rs6000_mtfsf (GEN_INT (0x01), tmp_df));
+      emit_insn (gen_rs6000_update_fpscr_rn_field (tmp_rn));
     }
   DONE;
 })
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index cdbd4b34a35..c6ad3a44d26 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -18188,7 +18188,6 @@ double __builtin_mffs (void);
 void __builtin_mtfsf (const int, double);
 void __builtin_mtfsb0 (const int);
 void __builtin_mtfsb1 (const int);
-void __builtin_set_fpscr_rn (int);
 @end smallexample
 
 The @code{__builtin_ppc_get_timebase} and @code{__builtin_ppc_mftb}
@@ -18209,13 +18208,23 @@ values to selected fields of the FPSCR.  The
 as an argument.  The valid bit range is between 0 and 31.  The builtins map to
 the @code{mtfsb0} and @code{mtfsb1} instructions which take the argument and
 add 32.  Hence these instructions only modify the FPSCR[32:63] bits by
-changing the specified bit to a zero or one respectively.  The
-@code{__builtin_set_fpscr_rn} builtin allows changing both of the floating
-point rounding mode bits.  The argument is a 2-bit value.  The argument can
-either be a @code{const int} or stored in a variable. The builtin uses
-the ISA 3.0
-instruction @code{mffscrn} if available, otherwise it reads the FPSCR, masks
-the current rounding mode bits out and OR's in the new value.
+changing the specified bit to a zero or one respectively.
+
+@smallexample
+double __builtin_set_fpscr_rn (int);
+double __builtin_set_fpscr_rn (double);
+@end smallexample
+
+The @code{__builtin_set_fpscr_rn} builtin allows changing both of the floating
+point rounding mode bits and returning the various FPSCR fields before the RN
+field is updated.  The builtin returns a double consisting of the initial value
+of the FPSCR fields DRN, VE, OE, UE, ZE, XE, NI, and RN bit positions with all
+other bits set to zero. The builtin argument is a 2-bit value for the new RN
+field value.  The argument can either be a @code{const int} or stored in a
+variable. Additionally, the argument can be a variable of type double. The
+builtin uses the ISA 3.0 instruction @code{mffscrn} if available, otherwise it
+reads the FPSCR, masks the current rounding mode bits out and OR'sin the new
+RN field value.
 
 @node Basic PowerPC Built-in Functions Available on ISA 2.05
 @subsubsection Basic PowerPC Built-in Functions Available on ISA 2.05
diff --git a/gcc/testsuite/gcc.target/powerpc/test_fpscr_rn_builtin.c b/gcc/testsuite/gcc.target/powerpc/test_fpscr_rn_builtin.c
index 04707ad8a56..1522983102d 100644
--- a/gcc/testsuite/gcc.target/powerpc/test_fpscr_rn_builtin.c
+++ b/gcc/testsuite/gcc.target/powerpc/test_fpscr_rn_builtin.c
@@ -6,11 +6,70 @@
 #endif
 
 #define RN_MASK  0x3LL             /* RN field mask */
+#define FIELD_MASK 0x00000007000000FFULL
+
+union blah {
+  double d;
+  unsigned long long ll;
+} conv_val;
 
 void abort (void);
-void __attribute__ ((noipa)) wrap_set_fpscr_rn (int val)
+double __attribute__ ((noipa)) wrap_set_fpscr_rn (int val)
 {
-  __builtin_set_fpscr_rn (val);
+  return __builtin_set_fpscr_rn (val);
+}
+
+double __attribute__ ((noipa)) wrap_set_fpscr_rn_d (double val)
+{
+  return __builtin_set_fpscr_rn (val);
+}
+
+double __attribute__ ((noipa)) wrap_const_fpscr_rn (int val)
+{
+  switch (val)
+    {
+    case 0: return __builtin_set_fpscr_rn (0x0);
+    case 1: return __builtin_set_fpscr_rn (0x1);
+    case 2: return __builtin_set_fpscr_rn (0x2);
+    case 3: return __builtin_set_fpscr_rn (0x3);
+    }
+}
+
+void check_builtin_set_fpscr_rn (unsigned long long initial_fpscr,
+				 int new_RN, double result)
+{
+  register double  f14;
+  unsigned long long masked_fpscr = initial_fpscr & FIELD_MASK;
+  
+  conv_val.d = result;
+
+  /* Check the result.  */
+  if (conv_val.ll != masked_fpscr)
+    {
+#ifdef DEBUG
+       printf("ERROR, __builtin_set_fpscr_rn(%d) did not return expected value %llx.\n",
+	      new_RN, masked_fpscr);
+       printf("fpscr_val_initial = 0x%llx\n", initial_fpscr);       
+       printf("result = 0x%llx\n", conv_val.ll);
+#else
+       abort();
+#endif
+    }
+
+  /* Check to see if the RN field was updated.  */
+    __asm __volatile ("mffs %0" : "=f"(f14));
+  conv_val.d = f14;
+
+  if ((conv_val.ll & RN_MASK) != new_RN)
+#ifdef DEBUG
+    {
+      printf("ERROR,  __builtin_set_fpscr_rn(%d) did not update RN to %llx.\n",
+	     new_RN, new_RN);
+      printf("  conv_val.ll = 0x%llx\n", conv_val.ll);
+    }
+#else
+    abort();
+#endif
 }
 
 int main ()
@@ -18,12 +77,10 @@ int main ()
   int i;
   int val, bit;
   double fpscr_val;
-  union blah {
-    double d;
-    unsigned long long ll;
-  } conv_val;
+  unsigned long long fpscr_val_initial;
   
   unsigned long long ll_value;
+  union blah src_double;
   register double  f14;
 
   /* __builtin_set_fpscr_rn() builtin can take a const or a variable
@@ -190,4 +247,76 @@ int main ()
        abort();
 #endif
     }		  
-}
+
+
+  /* Test return value from __builtin_set_fpscr_rn. The fields (DRN, VE, OE,
+     UE, ZE, XE, NI, RN) are returned and  the RN field of FPSCR is updated
+     with the specified argument for the builtin.  */
+
+  /* Check immediate argument cases */
+  __asm __volatile ("mffs %0" : "=f"(f14));
+  conv_val.d = f14;
+  fpscr_val_initial = conv_val.ll;
+
+  val = 0x0;
+  fpscr_val = wrap_const_fpscr_rn (val);
+  check_builtin_set_fpscr_rn (fpscr_val_initial, val, fpscr_val);
+
+  __asm __volatile ("mffs %0" : "=f"(f14));
+  conv_val.d = f14;
+  fpscr_val_initial = conv_val.ll;
+
+  val = 0x3;
+  fpscr_val = wrap_const_fpscr_rn (val);
+  check_builtin_set_fpscr_rn (fpscr_val_initial, val, fpscr_val);
+  
+  /* Check int argument cases */
+  __asm __volatile ("mffs %0" : "=f"(f14));
+  conv_val.d = f14;
+  fpscr_val_initial = conv_val.ll;
+
+  val = 0x1;
+  fpscr_val = wrap_set_fpscr_rn (val);
+  check_builtin_set_fpscr_rn (fpscr_val_initial, val, fpscr_val);
+
+  __asm __volatile ("mffs %0" : "=f"(f14));
+  conv_val.d = f14;
+  fpscr_val_initial = conv_val.ll;
+
+  val = 0x2;
+  fpscr_val = wrap_set_fpscr_rn (val);
+  check_builtin_set_fpscr_rn (fpscr_val_initial, val, fpscr_val);
+
+  /* Check double  argument cases */
+  __asm __volatile ("mffs %0" : "=f"(f14));
+  conv_val.d = f14;
+  fpscr_val_initial = conv_val.ll;
+
+  conv_val.ll = 0x3;
+  fpscr_val = wrap_set_fpscr_rn_d (conv_val.d);
+  check_builtin_set_fpscr_rn (fpscr_val_initial, conv_val.ll, fpscr_val);
+
+  __asm __volatile ("mffs %0" : "=f"(f14));
+  conv_val.d = f14;
+  fpscr_val_initial = conv_val.ll;
+
+  conv_val.ll = 0x0;
+  fpscr_val = wrap_set_fpscr_rn_d (conv_val.d);
+  check_builtin_set_fpscr_rn (fpscr_val_initial, conv_val.ll, fpscr_val);
+  
+  __asm __volatile ("mffs %0" : "=f"(f14));
+  conv_val.d = f14;
+  fpscr_val_initial = conv_val.ll;
+
+  conv_val.ll = 0x1;
+  fpscr_val = wrap_set_fpscr_rn_d (conv_val.d);
+  check_builtin_set_fpscr_rn (fpscr_val_initial, conv_val.ll, fpscr_val);
+
+  __asm __volatile ("mffs %0" : "=f"(f14));
+  conv_val.d = f14;
+  fpscr_val_initial = conv_val.ll;
+
+  conv_val.ll = 0x2;
+  fpscr_val = wrap_set_fpscr_rn_d (conv_val.d);
+  check_builtin_set_fpscr_rn (fpscr_val_initial, conv_val.ll, fpscr_val);
+}		  
