diff --git a/gcc/config/rs6000/rs6000-builtins.def b/gcc/config/rs6000/rs6000-builtins.def
index 92d9b46e1b9..ae08d2fbff7 100644
--- a/gcc/config/rs6000/rs6000-builtins.def
+++ b/gcc/config/rs6000/rs6000-builtins.def
@@ -2849,6 +2849,14 @@
   const signed int  __builtin_vsx_scalar_extract_exp (double);
     VSEEDP xsxexpdp_si {}
 
+; Immediate instruction only uses the least significant two bits of the
+; const int.
+  double __builtin_mffscrni (const int<2>);
+    MFFSCRNI rs6000_mffscrni {nosoft}
+
+  double __builtin_mffscrnd (double);
+    MFFSCRNF rs6000_mffscrn {nosoft}
+
 [power9-64]
   void __builtin_altivec_xst_len_r (vsc, void *, long);
     XST_LEN_R xst_len_r {}
@@ -2875,7 +2883,6 @@
   pure vsc __builtin_vsx_xl_len_r (void *, signed long);
     XL_LEN_R xl_len_r {}
 
-
 ; Builtins requiring hardware support for IEEE-128 floating-point.
 [ieee128-hw]
   fpmath _Float128 __builtin_addf128_round_to_odd (_Float128, _Float128);
diff --git a/gcc/config/rs6000/rs6000-overload.def b/gcc/config/rs6000/rs6000-overload.def
index 26dc662b8fb..39423bcec2b 100644
--- a/gcc/config/rs6000/rs6000-overload.def
+++ b/gcc/config/rs6000/rs6000-overload.def
@@ -78,6 +78,11 @@
 ; like after a required newline, but nowhere else.  Lines beginning with
 ; a semicolon are also treated as blank lines.
 
+[MFFSCR, __builtin_mffscrn, __builtin_mffscrn]
+  double __builtin_mffscrn (const int<2>);
+    MFFSCRNI
+  double __builtin_mffscrn (double);
+    MFFSCRNF
 
 [BCDADD, __builtin_bcdadd, __builtin_vec_bcdadd]
   vsq __builtin_vec_bcdadd (vsq, vsq, const int);
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ed8b9c8a87b..82f9932666a 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -18274,6 +18274,16 @@ The @code{__builtin_recipdiv}, and @code{__builtin_recipdivf}
 functions generate multiple instructions to implement division using
 the reciprocal estimate instructions.
 
+double __builtin_mffscrn (const int);
+double __builtin_mffscrn (double);
+
+The @code{__builtin_mffscrn} returns the contents of the control bits DRN, VE,
+OE, UE, ZE, XE, NI, RN in the FPSCR are returned with RN updated appropriately.
+In the case of the const int variant of the builtin, RN is set to the 2-bit
+value specified in the builtin.  In the case of the double builtin variant, the
+2-bit value in the double argument that corresponds to the RN location in the
+FPSCR is updated.
+
 The following functions require @option{-mhard-float} and
 @option{-mmultiple} options.
 
diff --git a/gcc/testsuite/gcc.target/powerpc/builtin-mffscrn.c b/gcc/testsuite/gcc.target/powerpc/builtin-mffscrn.c
new file mode 100644
index 00000000000..69a7a17cfc7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/builtin-mffscrn.c
@@ -0,0 +1,106 @@
+/* { dg-do run } */
+/* { dg-require-effective-target p9modulo_hw } */
+/* { dg-options "-mdejagnu-cpu=power9" } */
+
+#include <altivec.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#define MASK 0x3
+#define EXPECTED1 0x1
+#define EXPECTED2 0x2
+
+void abort (void);
+
+int
+main()
+{
+  unsigned long mask, result, expected;
+  double double_arg;
+  
+  union convert_t {
+    double d;
+    unsigned long ul;
+  } val;
+
+  /* Test immediate version of __builtin_mffscrn. */
+  /* Read FPSCR and set RN bits in FPSCR[62:63]. */
+  val.d = __builtin_mffscrn (EXPECTED2);
+
+  /* Read FPSCR, bits [62:63] should have been set to 0x2 by previous builtin
+     call.  */
+  val.d = __builtin_mffscrn (EXPECTED1);
+  /* The expected result is the argument for the previous call to
+     __builtin_mffscrn.  */
+  expected = EXPECTED2;
+  result = MASK & val.ul;
+
+  if (EXPECTED2 != result)
+#ifdef DEBUG
+    printf("Result of mffscrn immediate doesn't match EXPECTED2.  Result was 0x%lx\n",
+	   result);
+#else
+    abort();
+#endif
+
+  /* Read FPSCR, bits [62:63] should have been set to 0x1 by previous builtin
+   call*/
+  val.d = __builtin_mffscrn (EXPECTED1);
+  expected = EXPECTED1;
+  result = MASK & val.ul;
+
+  if (EXPECTED1 != result)
+#ifdef DEBUG
+  printf("Result of mffscrn immediate doesn't match EXPECTED1.  Result was 0x%lx\n",
+	 result);
+#else
+  abort();
+#endif
+
+
+  /* Test double argument version of __builtin_mffscrn */
+  val.ul = EXPECTED2;
+  double_arg = val.d;
+
+  /* Read FPSCR and set RN bits in FPSCR[62:63]. */
+  val.d = __builtin_mffscrn (double_arg);
+
+  /* Read FPSCR, bits [62:63] should have been set to 0x2 by previous builtin  
+     call.  */
+
+  val.ul = EXPECTED1;
+  double_arg = val.d;
+
+  val.d = __builtin_mffscrn (double_arg);
+  /* The expected result is the argument for the previous call to
+     __builtin_mffscrn.  */
+  expected = EXPECTED2;
+  result = MASK & val.ul;
+
+  if (EXPECTED2 != result)
+#ifdef DEBUG
+    printf("Result of mffscrn double arg doesn't match EXPECTED2.  Result was 0x%lx\n",
+	   result);
+#else
+    abort();
+#endif
+
+  /* Read FPSCR, bits [62:63] should have been set to 0x1 by previous builtin
+   call*/
+  val.ul = EXPECTED1;
+  double_arg = val.d;
+
+  val.d = __builtin_mffscrn (double_arg);
+  expected = EXPECTED1;
+  result = MASK & val.ul;
+
+  if (EXPECTED1 != result)
+#ifdef DEBUG
+    printf("Result of mffscrn double arg doesn't match EXPECTED1.  Result was 0x%lx\n",
+	   result);
+#else
+    abort();
+#endif
+}
