[v6] rtl: builtins: (not just) rs6000: Add builtins for fegetround, feclearexcept and feraiseexcept [PR94193]

Message ID 20211017000415.vqt5yyrq7j7kg5c4@work-tp
State New
Headers
Series [v6] rtl: builtins: (not just) rs6000: Add builtins for fegetround, feclearexcept and feraiseexcept [PR94193] |

Commit Message

Raoni Fassina Firmino Oct. 17, 2021, 12:04 a.m. UTC
  Changes since v5[5]:
  - Reworked all builtins to accept the FE_* macros as parameters and
    so be agnostic to libc implementations.  Largely based of
    fpclassify.  To that end, there is some new files changed:
    + Change the argument list for the builtins declarations in
      builtins.def
    + Added new types in builtin-types.def to use in the buitins
      declarations.
    + Added extra documentation for the builtins on doc/extend.texi,
      similar to fpclassify.
  - Updated doc/md.texi documentation with the new optab behaviors.
  - Updated comments to the expanders and expand handlers to try to
    explain whats is going on.
  - Changed the description for the return operand in the RTL template
    of the fegetround expander.  Using "(set )", the same way as
    rs6000_mffsl expander.
  - Updated testcases with helper macros with the new argument list.

Tested on top of master (f7571527a44808cd7062c77bb9570c13f4f6a126)
on the following plataforms with no regression:
  - powerpc64le-linux-gnu (Power 9)
  - powerpc64le-linux-gnu (Power 8)
  - powerpc64-linux-gnu (Power 9, with 32 and 64 bits tests)

Documentation changes tested on x86_64-redhat-linux.

This approach left me with some hanging problems that I am not quite
sure how to go about it.

First is the different arguments from the C99 functions.  I think the
solution is a macro to correct this, like so:

    #define feclearexcept(excepts) \
        __builtin_feclearexcept(excepts, FE_DIVBYZERO, FE_INEXACT, \
                                FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW)

That is automatically always included or included when fenv.h is
included.  Does the preprocessor have this ability? If so, where
should I put it?

But this solution seems to have a problem that it will bypass
-fno-builtin, unless there can be a conditional check for it on the
preprocessor.

Second is the fallback of the expanders.  When the expanders fail it
will leave the function call, which is great, but since the argument
list is different, well, it not is pretty.  There is no execution
problem, since the builtins only having extra arguments, but it
generate extra unnecessary code. Here is an example, a snipped of the
generated assembly from the builtin-feclearexcept-feraiseexcept-1.c
testcase:

    # builtin-feclearexcept-feraiseexcept-1.c:36: __builtin_feclearexcept(FE_INVALID);  // CALL
    lis %r8,0x800
    lis %r7,0x1000
    lis %r6,0x2000
    lis %r5,0x200
    lis %r4,0x400
    lis %r3,0x2000
    bl feclearexcept

I assume I can modify the RTL to remove the unneeded arguments.  If
so, there is pointer where this is done or how I can do it?

I'm also adding some extra documentation for the builtins section, the
doc/extend.texi, close to where the fpclassify documentation is[6],
but I don't know if I'm doing it right, especially changing such a
front facing documentation.

I'm repeating the "changelog" from past versions here for convenience:

Changes since v4[4]:
  - Fixed more spelling and code style.
  - Add more clarification on  comments for feraiseexcept and
    feclearexcept expands;

Changes since v3[3]:
  - Fixed fegetround bug on powerpc64 (big endian) that Segher
    spotted;

Changes since v2[2]:
  - Added documentation for the new optabs;
  - Remove use of non portable __builtin_clz;
  - Changed feclearexcept and feraiseexcept to accept all 4 valid
    flags at the same time and added more test for that case;
  - Extended feclearexcept and feraiseexcept testcases to match
    accepting multiple flags;
  - Fixed builtin-feclearexcept-feraiseexcept-2.c testcase comparison
    after feclearexcept tests;
  - Updated commit message to reflect change in feclearexcept and
    feraiseexcept from the glibc conterpart;
  - Fixed English spelling and typos;
  - Fixed code-style;
  - Changed subject line tag to make clear it is not just rs6000 code.

Changes since v1[1]:
  - Fixed english spelling;
  - Fixed code-style;
  - Changed match operand predicate in feclearexcept and feraiseexcept;
  - Changed testcase options;
  - Minor changes in test code to be C90 compatible;
  - Other minor changes sugested by Segher;
  - Changed subject line tag (not sure if I tagged correctly or should
    include optabs: also)

[1] https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552024.html
[2] https://gcc.gnu.org/pipermail/gcc-patches/2020-September/553297.html
[3] https://gcc.gnu.org/pipermail/gcc-patches/2020-October/557109.html
[4] https://gcc.gnu.org/pipermail/gcc-patches/2020-October/557349.html
[5] https://gcc.gnu.org/pipermail/gcc-patches/2020-November/557984.html
[6] https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#Other-Builtins

---- 8< ----

This optimizations were originally in glibc, but was removed
and suggested that they were a good fit as gcc builtins[1].

feclearexcept and feraiseexcept were extended (in comparison to the
glibc version) to accept any combination of the accepted flags, not
limited to just one flag bit at a time anymore.

There is signature changes to all three builtins relative to the
correspondent libc function, the extra arguments add a way to make the
builtins work agnostic of the libc implementations.

The associated bugreport: PR target/94193

[1] https://sourceware.org/legacy-ml/libc-alpha/2020-03/msg00047.html
    https://sourceware.org/legacy-ml/libc-alpha/2020-03/msg00080.html

2020-08-13  Raoni Fassina Firmino  <raoni@linux.ibm.com>

gcc/ChangeLog:

        * builtin-types.def (BT_FN_INT_INT_INT_INT_INT): New type.
        (BT_FN_INT_INT_INT_INT_INT_INT_INT): New type.
        * builtins.c (expand_builtin_fegetround): New function.
        (expand_builtin_feclear_feraise_except): New function.
        (expand_builtin): Add cases for BUILT_IN_FEGETROUND,
        BUILT_IN_FECLEAREXCEPT and BUILT_IN_FERAISEEXCEPT
        * builtins.def (BUILT_IN_FECLEAREXCEPT): Change type.
        (BUILT_IN_FEGETROUND): Change type.
        (BUILT_IN_FERAISEEXCEPT): Change type.
        * config/rs6000/rs6000.md (fegetroundsi): New pattern.
        (feclearexceptsi): New Pattern.
        (feraiseexceptsi): New Pattern.
        * doc/extend.texi: Add a new introductory paragraph about the
        new builtins.
        (__builtin_feclearexcept): Document new builtin.
        (__builtin_feclearexcept): Document new builtin.
        (__builtin_feraiseexcept): Document new builtin.
        * doc/md.texi: (fegetround@var{m}): Document new optab.
        (feclearexcept@var{m}): Document new optab.
        (feraiseexcept@var{m}): Document new optab.
        * optabs.def (fegetround_optab): New optab.
        (feclearexcept_optab): New optab.
        (feraiseexcept_optab): New optab.

gcc/testsuite/ChangeLog:

        * gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-1.c: New test.
        * gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-2.c: New test.
        * gcc.target/powerpc/builtin-fegetround.c: New test.

Signed-off-by: Raoni Fassina Firmino <raoni@linux.ibm.com>
---
 gcc/builtin-types.def                         |   4 +
 gcc/builtins.c                                | 100 +++++++++
 gcc/builtins.def                              |   6 +-
 gcc/config/rs6000/rs6000.md                   | 163 ++++++++++++++
 gcc/doc/extend.texi                           |  51 +++++
 gcc/doc/md.texi                               |  21 ++
 gcc/optabs.def                                |   4 +
 .../builtin-feclearexcept-feraiseexcept-1.c   |  82 +++++++
 .../builtin-feclearexcept-feraiseexcept-2.c   | 209 ++++++++++++++++++
 .../gcc.target/powerpc/builtin-fegetround.c   |  39 ++++
 10 files changed, 676 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-1.c
 create mode 100644 gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-2.c
 create mode 100644 gcc/testsuite/gcc.target/powerpc/builtin-fegetround.c
  

Comments

Joseph Myers Oct. 18, 2021, 3:54 p.m. UTC | #1
On Sun, 17 Oct 2021, Raoni Fassina Firmino wrote:

> First is the different arguments from the C99 functions.  I think the
> solution is a macro to correct this, like so:
> 
>     #define feclearexcept(excepts) \
>         __builtin_feclearexcept(excepts, FE_DIVBYZERO, FE_INEXACT, \
>                                 FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW)
> 
> That is automatically always included or included when fenv.h is
> included.  Does the preprocessor have this ability? If so, where
> should I put it?

The compiler should not be adding such macros to libc headers.  If libc 
wants to provide such optimizations based on built-in functions, they 
should go in libc headers with an appropriate condition on the compiler 
version.

However, it's better to get things right automatically without needing any 
macros or other header additions at all.  That is, define feclearexcept as 
a built-in function, *without* the extra arguments, and with the back end 
knowing about the FE_* values for the target libc.  Then you can simply 
avoid expanding the function inline when the back end doesn't know both 
the FE_* values and how to use them.

fpclassify is a fundamentally different case, because that's defined by 
the standard to be a *type-generic macro*, whereas feclearexcept is 
defined by the standard to be a *function*.  The example of fpclassify 
should not be followed for feclearexcept, feraiseexcept or fegetround.

> Second is the fallback of the expanders.  When the expanders fail it
> will leave the function call, which is great, but since the argument
> list is different, well, it not is pretty.  There is no execution

If you define __builtin_X to have different arguments to X, it should also 
be defined so it's *always* expanded inline (on all architectures) and 
never falls back to a library function call to X.  (This means, for 
example, not defining __builtin_X at all as a built-in function in cases, 
such as soft float, where you can't expand it inline, so that erroneous 
code trying to call __builtin_X in that case ends up with an undefined 
reference to the __builtin_X symbol.)

Once you avoid having different arguments to the library function, you can 
simply avoid expanding inline whenever the back end lacks the relevant 
information; you don't need to do anything to avoid the built-in function 
existing.

> +@deftypefn {Built-in Function} int __builtin_fegetround (int, int, int, int)
> +This built-in implements the C99 fegetround functionality.  The four int
> +arguments should be the target library's notion of the possible FP rouding
> +modes.  They must be constant values and they must appear in this order:
> +@code{FE_DOWNWARD}, @code{FE_TONEAREST}, @code{FE_TOWARDZERO},
> +@code{FE_UPWARD}.  In other words:

Some architectures have more rounding modes (e.g. FE_TONEARESTFROMZERO).  
Some have fewer.  I think that illustrates the essential flaw of defining 
these functions to take a fixed set of rounding mode macros as arguments.

On the other hand, there is a use for a *different* built-in function to 
get the rounding mode for FLT_ROUNDS, using the fixed set of values for 
FLT_ROUNDS specified in the C standard, and *always expanding inline 
without ever introducing a dependency on libm*.  See bug 30569 and 
<https://gcc.gnu.org/legacy-ml/gcc/2013-11/msg00317.html> regarding that.
  
Raoni Fassina Firmino Nov. 24, 2021, 8:22 p.m. UTC | #2
Hi Joseph,

Thanks for the detailed review and explanations.

On Mon, Oct 18, 2021 at 03:54:53PM +0000, Joseph Myers wrote:
> However, it's better to get things right automatically without needing any 
> macros or other header additions at all.  That is, define feclearexcept as 
> a built-in function, *without* the extra arguments, and with the back end 
> knowing about the FE_* values for the target libc.  Then you can simply 
> avoid expanding the function inline when the back end doesn't know both 
> the FE_* values and how to use them.

I took this part to heart, I agree that an approach in molds of the v5
sounds more like it, something that will "just works" or fallback
gracefully. And who knew, with your insight I think I find just
the thing, I am finishing a v7 and I hope it address the previews
consernings and sidestep all this complications with the v6 aproach.


o/
Raoni
  
Segher Boessenkool Nov. 24, 2021, 9:32 p.m. UTC | #3
On Wed, Nov 24, 2021 at 05:22:57PM -0300, Raoni Fassina Firmino wrote:
> Hi Joseph,
> 
> Thanks for the detailed review and explanations.

From me as well :-)

> On Mon, Oct 18, 2021 at 03:54:53PM +0000, Joseph Myers wrote:
> > However, it's better to get things right automatically without needing any 
> > macros or other header additions at all.  That is, define feclearexcept as 
> > a built-in function, *without* the extra arguments, and with the back end 
> > knowing about the FE_* values for the target libc.  Then you can simply 
> > avoid expanding the function inline when the back end doesn't know both 
> > the FE_* values and how to use them.
> 
> I took this part to heart, I agree that an approach in molds of the v5
> sounds more like it, something that will "just works" or fallback
> gracefully. And who knew, with your insight I think I find just
> the thing, I am finishing a v7 and I hope it address the previews
> consernings and sidestep all this complications with the v6 aproach.

What you will lose this way is that it will not work on any C library
that doesn't have explicit support.  Which is a shame, but it seems we
cannot avoid this.  Especially the "fesetround should be a function, not
a macro" argument is a showstopper :-/

Thanks,


Segher
  

Patch

diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def
index d160826e1d4f..0520772801e6 100644
--- a/gcc/builtin-types.def
+++ b/gcc/builtin-types.def
@@ -680,6 +680,8 @@  DEF_FUNCTION_TYPE_4 (BT_FN_BOOL_UINT_ULLPTR_ULLPTR_ULLPTR,
 		     BT_PTR_ULONGLONG)
 DEF_FUNCTION_TYPE_4 (BT_FN_VOID_UINT_PTR_INT_PTR, BT_VOID, BT_INT, BT_PTR,
 		     BT_INT, BT_PTR)
+DEF_FUNCTION_TYPE_4 (BT_FN_INT_INT_INT_INT_INT,
+		     BT_INT, BT_INT, BT_INT, BT_INT, BT_INT)
 
 DEF_FUNCTION_TYPE_5 (BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VALIST_ARG,
 		     BT_INT, BT_STRING, BT_INT, BT_SIZE, BT_CONST_STRING,
@@ -737,6 +739,8 @@  DEF_FUNCTION_TYPE_6 (BT_FN_BOOL_VPTR_PTR_I16_BOOL_INT_INT,
 		     BT_INT)
 DEF_FUNCTION_TYPE_6 (BT_FN_BOOL_SIZE_VPTR_PTR_PTR_INT_INT, BT_BOOL, BT_SIZE,
 		     BT_VOLATILE_PTR, BT_PTR, BT_PTR, BT_INT, BT_INT)
+DEF_FUNCTION_TYPE_6 (BT_FN_INT_INT_INT_INT_INT_INT_INT,
+		     BT_INT, BT_INT, BT_INT, BT_INT, BT_INT, BT_INT, BT_INT)
 
 DEF_FUNCTION_TYPE_7 (BT_FN_VOID_OMPFN_PTR_UINT_LONG_LONG_LONG_UINT,
 		     BT_VOID, BT_PTR_FN_VOID_PTR, BT_PTR, BT_UINT,
diff --git a/gcc/builtins.c b/gcc/builtins.c
index f1c3fea3583d..da51ed25711a 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -119,6 +119,9 @@  static rtx expand_builtin_mathfn_3 (tree, rtx, rtx);
 static rtx expand_builtin_mathfn_ternary (tree, rtx, rtx);
 static rtx expand_builtin_interclass_mathfn (tree, rtx);
 static rtx expand_builtin_sincos (tree);
+static rtx expand_builtin_fegetround (tree, rtx, machine_mode);
+static rtx expand_builtin_feclear_feraise_except (tree, rtx, machine_mode,
+						  optab);
 static rtx expand_builtin_cexpi (tree, rtx);
 static rtx expand_builtin_int_roundingfn (tree, rtx);
 static rtx expand_builtin_int_roundingfn_2 (tree, rtx);
@@ -2527,6 +2530,83 @@  expand_builtin_sincos (tree exp)
   return const0_rtx;
 }
 
+/* Expand call EXP to __builtin_fegetround(int, int, int, int), returning the
+   result and setting it in TARGET.  Otherwise return NULL_RTX on failure.
+   This builtin implements fegetround (from C99 fenv.h) and will generate code
+   to return the current floating point rounding mode.  The possible return
+   values must be supplied as int arguments to the call in the following order:
+   FE_DOWNWARD, FE_TONEAREST, FE_TOWARDZERO and FE_UPWARD. This enables the
+   builtin to work agnostic of the c library's values for the rounding modes. */
+static rtx
+expand_builtin_fegetround (tree exp, rtx target, machine_mode target_mode)
+{
+  if (!validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE,
+			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  insn_code icode = direct_optab_handler (fegetround_optab, SImode);
+  if (icode == CODE_FOR_nothing)
+    return NULL_RTX;
+
+  if (target == 0
+      || GET_MODE (target) != target_mode
+      || !(*insn_data[icode].operand[0].predicate) (target, target_mode))
+    target = gen_reg_rtx (target_mode);
+
+  rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
+  rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
+  rtx op2 = expand_normal (CALL_EXPR_ARG (exp, 2));
+  rtx op3 = expand_normal (CALL_EXPR_ARG (exp, 3));
+
+  rtx pat = GEN_FCN (icode) (target, op0, op1, op2, op3);
+  if (!pat)
+    return NULL_RTX;
+  emit_insn (pat);
+
+  return target;
+}
+
+/* Expand call EXP to __builtin_feclearexcept(int, int, int, int, int, int) or
+   or __builtin_feraiseexcept(int, int, int, int, int, int), returning the
+   result and setting it in TARGET.  Otherwise return NULL_RTX on failure.
+   This builtins implements feclearexcept and feraiseexcept (from C99 fenv.h)
+   and will generate code to clear or raise floating point exceptions supplied
+   as the first argument.  The possible exceptions values must be supplied as
+   the last five int arguments to the call in the following order: FE_DIVBYZERO,
+   FE_INEXACT, FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW. This enables the
+   builtin work agnostic of the c library's values for the exceptions. */
+static rtx
+expand_builtin_feclear_feraise_except (tree exp, rtx target,
+				       machine_mode target_mode, optab op_optab)
+{
+  if (!validate_arglist (exp, INTEGER_TYPE, INTEGER_TYPE, INTEGER_TYPE,
+			 INTEGER_TYPE, INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  insn_code icode = direct_optab_handler (op_optab, SImode);
+  if (icode == CODE_FOR_nothing)
+    return NULL_RTX;
+
+  if (target == 0
+      || GET_MODE (target) != target_mode
+      || !(*insn_data[icode].operand[0].predicate) (target, target_mode))
+    target = gen_reg_rtx (target_mode);
+
+  rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
+  rtx op1 = expand_normal (CALL_EXPR_ARG (exp, 1));
+  rtx op2 = expand_normal (CALL_EXPR_ARG (exp, 2));
+  rtx op3 = expand_normal (CALL_EXPR_ARG (exp, 3));
+  rtx op4 = expand_normal (CALL_EXPR_ARG (exp, 4));
+  rtx op5 = expand_normal (CALL_EXPR_ARG (exp, 5));
+
+  rtx pat = GEN_FCN (icode) (target, op0, op1, op2, op3, op4, op5);
+  if (!pat)
+    return NULL_RTX;
+  emit_insn (pat);
+
+  return target;
+}
+
 /* Expand a call to the internal cexpi builtin to the sincos math function.
    EXP is the expression that is a call to the builtin function; if convenient,
    the result should be placed in TARGET.  */
@@ -7033,6 +7113,26 @@  expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
 	return target;
       break;
 
+    case BUILT_IN_FEGETROUND:
+      target = expand_builtin_fegetround (exp, target, target_mode);
+      if (target)
+	return target;
+      break;
+
+    case BUILT_IN_FECLEAREXCEPT:
+      target = expand_builtin_feclear_feraise_except (exp, target, target_mode,
+						      feclearexcept_optab);
+      if (target)
+	return target;
+      break;
+
+    case BUILT_IN_FERAISEEXCEPT:
+      target = expand_builtin_feclear_feraise_except (exp, target, target_mode,
+						      feraiseexcept_optab);
+      if (target)
+	return target;
+      break;
+
     case BUILT_IN_APPLY_ARGS:
       return expand_builtin_apply_args ();
 
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 45a09b4d42de..65157f3e21c0 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -364,12 +364,12 @@  DEF_C2X_BUILTIN        (BUILT_IN_FABSD128, "fabsd128", BT_FN_DFLOAT128_DFLOAT128
 DEF_C99_BUILTIN        (BUILT_IN_FDIM, "fdim", BT_FN_DOUBLE_DOUBLE_DOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO)
 DEF_C99_BUILTIN        (BUILT_IN_FDIMF, "fdimf", BT_FN_FLOAT_FLOAT_FLOAT, ATTR_MATHFN_FPROUNDING_ERRNO)
 DEF_C99_BUILTIN        (BUILT_IN_FDIML, "fdiml", BT_FN_LONGDOUBLE_LONGDOUBLE_LONGDOUBLE, ATTR_MATHFN_FPROUNDING_ERRNO)
-DEF_C99_BUILTIN        (BUILT_IN_FECLEAREXCEPT, "feclearexcept", BT_FN_INT_INT, ATTR_NOTHROW_LEAF_LIST)
+DEF_C99_BUILTIN        (BUILT_IN_FECLEAREXCEPT, "feclearexcept", BT_FN_INT_INT_INT_INT_INT_INT_INT, ATTR_NOTHROW_LEAF_LIST)
 DEF_C99_BUILTIN        (BUILT_IN_FEGETENV, "fegetenv", BT_FN_INT_FENV_T_PTR, ATTR_NOTHROW_LEAF_LIST)
 DEF_C99_BUILTIN        (BUILT_IN_FEGETEXCEPTFLAG, "fegetexceptflag", BT_FN_INT_FEXCEPT_T_PTR_INT, ATTR_NOTHROW_LEAF_LIST)
-DEF_C99_BUILTIN        (BUILT_IN_FEGETROUND, "fegetround", BT_FN_INT, ATTR_PURE_NOTHROW_LEAF_LIST)
+DEF_C99_BUILTIN        (BUILT_IN_FEGETROUND, "fegetround", BT_FN_INT_INT_INT_INT_INT, ATTR_PURE_NOTHROW_LEAF_LIST)
 DEF_C99_BUILTIN        (BUILT_IN_FEHOLDEXCEPT, "feholdexcept", BT_FN_INT_FENV_T_PTR, ATTR_NOTHROW_LEAF_LIST)
-DEF_C99_BUILTIN        (BUILT_IN_FERAISEEXCEPT, "feraiseexcept", BT_FN_INT_INT, ATTR_NULL)
+DEF_C99_BUILTIN        (BUILT_IN_FERAISEEXCEPT, "feraiseexcept", BT_FN_INT_INT_INT_INT_INT_INT_INT, ATTR_NULL)
 DEF_C99_BUILTIN        (BUILT_IN_FESETENV, "fesetenv", BT_FN_INT_CONST_FENV_T_PTR, ATTR_NOTHROW_LEAF_LIST)
 DEF_C99_BUILTIN        (BUILT_IN_FESETEXCEPTFLAG, "fesetexceptflag", BT_FN_INT_CONST_FEXCEPT_T_PTR_INT, ATTR_NULL)
 DEF_C99_BUILTIN        (BUILT_IN_FESETROUND, "fesetround", BT_FN_INT_INT, ATTR_NOTHROW_LEAF_LIST)
diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index 6bec2bddbdee..1161aa831258 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -6860,6 +6860,169 @@ 
   [(set_attr "type" "fpload")
    (set_attr "length" "8")
    (set_attr "isa" "*,p8v,p8v")])
+
+;; int __builtin_fegetround(int, int, int, int)
+;;
+;; This built-in implements the C99 fegetround functionality.
+;; The four int arguments should be the target library's notion of the
+;; possible FP rouding modes.  They must be constant values and they
+;; must appear in this order: FE_DOWNWARD, FE_TONEAREST,
+;; FE_TOWARDZERO, FE_UPWARD.  In other words:
+;;
+;;     __builtin_fegetround(FE_DOWNWARD, FE_TONEAREST,
+;;                          FE_TOWARDZERO, FE_UPWARD)
+;;
+;; This values are used to match the processor values for the rounding
+;; mode with the target library's.  For now, to avoid the cost of
+;; converting between them, the behavior is to not expand if they are
+;; different and fallback to a call to libc.
+(define_expand "fegetroundsi"
+  [(use (match_operand:SI 4 "const_int_operand" "n"))
+   (use (match_operand:SI 3 "const_int_operand" "n"))
+   (use (match_operand:SI 2 "const_int_operand" "n"))
+   (use (match_operand:SI 1 "const_int_operand" "n"))
+   (set (match_operand:SI 0 "gpc_reg_operand")
+	(unspec_volatile:SI [(const_int 0)] UNSPECV_MFFSL))]
+  "TARGET_HARD_FLOAT"
+{
+  unsigned int fe_downward = INTVAL (operands[1]);
+  unsigned int fe_tonearest = INTVAL (operands[2]);
+  unsigned int fe_towardzero = INTVAL (operands[3]);
+  unsigned int fe_upward = INTVAL (operands[4]);
+
+  if (fe_downward != 3
+      || fe_tonearest != 0
+      || fe_towardzero != 1
+      || fe_upward != 2)
+    FAIL;
+
+  rtx tmp_df = gen_reg_rtx (DFmode);
+  emit_insn (gen_rs6000_mffsl (tmp_df));
+
+  rtx tmp_di = simplify_gen_subreg (DImode, tmp_df, DFmode, 0);
+  rtx tmp_di_2 = gen_reg_rtx (DImode);
+  emit_insn (gen_anddi3 (tmp_di_2, tmp_di, GEN_INT (3)));
+  rtx tmp_si = gen_reg_rtx (SImode);
+  tmp_si = gen_lowpart (SImode, tmp_di_2);
+  emit_move_insn (operands[0], tmp_si);
+  DONE;
+})
+
+;; int __builtin_feclearexcept(int, int, int, int, int, int)
+;;
+;; This built-in implements the C99 feclearexcept functionality.  The
+;; first argument is the original feclearexcept() EXCEPTS argument.
+;; The other five int arguments should be the target library's notion
+;; of the possible FP exceptions.  They must be constant values and
+;; they must appear in this order: FE_DIVBYZERO, FE_INEXACT,
+;; FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW.  In other words:
+;;
+;;     __builtin_feclearexcept(excepts, FE_DIVBYZERO, FE_INEXACT,
+;;                             FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW)
+;;
+;; This values are used to match the processor values for the
+;; exception with the target library's.
+;; This expansion for the builtin only works when EXCEPTS (the last
+;; argument) is a constant known at compile time and specifies any one
+;; of  FE_INEXACT, FE_DIVBYZERO, FE_UNDERFLOW and FE_OVERFLOW flags.
+;; It doesn't handle values out of range, and always returns 0.
+;; Note that FE_INVALID is unsupported because it maps to more than
+;; one bit of the FPSCR register.
+;; Because of these restrictions, this only expands on the desired
+;; cases and fallback to a call to libc on any other case.
+(define_expand "feclearexceptsi"
+  [(use (match_operand:SI 6 "const_int_operand" "n"))
+   (use (match_operand:SI 5 "const_int_operand" "n"))
+   (use (match_operand:SI 4 "const_int_operand" "n"))
+   (use (match_operand:SI 3 "const_int_operand" "n"))
+   (use (match_operand:SI 2 "const_int_operand" "n"))
+   (use (match_operand:SI 1 "const_int_operand" "n"))
+   (set (match_operand:SI 0 "gpc_reg_operand") (const_int 0))]
+  "TARGET_HARD_FLOAT"
+{
+  unsigned int excepts = INTVAL (operands[1]);
+  unsigned int fe_divbyzero = INTVAL (operands[2]);
+  unsigned int fe_inexact = INTVAL (operands[3]);
+  unsigned int fe_overflow = INTVAL (operands[5]);
+  unsigned int fe_underflow = INTVAL (operands[6]);
+  unsigned int valid_excepts = fe_divbyzero
+			       | fe_inexact
+			       | fe_overflow
+			       | fe_underflow;
+
+  if (excepts != (excepts & valid_excepts))
+    FAIL;
+
+  if (excepts & fe_divbyzero)
+    emit_insn (gen_rs6000_mtfsb0 (gen_rtx_CONST_INT (SImode, 5)));
+  if (excepts & fe_inexact)
+    emit_insn (gen_rs6000_mtfsb0 (gen_rtx_CONST_INT (SImode, 6)));
+  if (excepts & fe_underflow)
+    emit_insn (gen_rs6000_mtfsb0 (gen_rtx_CONST_INT (SImode, 4)));
+  if (excepts & fe_overflow)
+    emit_insn (gen_rs6000_mtfsb0 (gen_rtx_CONST_INT (SImode, 3)));
+
+  emit_move_insn (operands[0], const0_rtx);
+  DONE;
+})
+
+;; int __builtin_feraiseexcept(int, int, int, int, int, int)
+;;
+;; This built-in implements the C99 feraiseexcept functionality.  The
+;; first argument is the original feraiseexcept() EXCEPTS argument.
+;; The other five int arguments should be the target library's notion
+;; of the possible FP exceptions.  They must be constant values and
+;; they must appear in this order: FE_DIVBYZERO, FE_INEXACT,
+;; FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW.  In other words:
+;;
+;;     __builtin_feraiseexcept(excepts, FE_DIVBYZERO, FE_INEXACT,
+;;                             FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW)
+;;
+;; This values are used to match the processor values for the
+;; exception with the target library's.
+;; This expansion for the builtin only works when EXCEPTS (the last
+;; argument) is a constant known at compile time and specifies any one
+;; of  FE_INEXACT, FE_DIVBYZERO, FE_UNDERFLOW and FE_OVERFLOW flags.
+;; It doesn't handle values out of range, and always returns 0.
+;; Note that FE_INVALID is unsupported because it maps to more than
+;; one bit of the FPSCR register.
+;; Because of these restrictions, this only expands on the desired
+;; cases and fallback to a call to libc on any other case.
+(define_expand "feraiseexceptsi"
+  [(use (match_operand:SI 6 "const_int_operand" "n"))
+   (use (match_operand:SI 5 "const_int_operand" "n"))
+   (use (match_operand:SI 4 "const_int_operand" "n"))
+   (use (match_operand:SI 3 "const_int_operand" "n"))
+   (use (match_operand:SI 2 "const_int_operand" "n"))
+   (use (match_operand:SI 1 "const_int_operand" "n"))
+   (set (match_operand:SI 0 "gpc_reg_operand") (const_int 0))]
+  "TARGET_HARD_FLOAT"
+{
+  unsigned int excepts = INTVAL (operands[1]);
+  unsigned int fe_divbyzero = INTVAL (operands[2]);
+  unsigned int fe_inexact = INTVAL (operands[3]);
+  unsigned int fe_overflow = INTVAL (operands[5]);
+  unsigned int fe_underflow = INTVAL (operands[6]);
+  unsigned int valid_excepts = fe_divbyzero
+			       | fe_inexact
+			       | fe_overflow
+			       | fe_underflow;
+
+  if (excepts != (excepts & valid_excepts))
+    FAIL;
+
+  if (excepts & fe_divbyzero)
+    emit_insn (gen_rs6000_mtfsb1 (gen_rtx_CONST_INT (SImode, 5)));
+  if (excepts & fe_inexact)
+    emit_insn (gen_rs6000_mtfsb1 (gen_rtx_CONST_INT (SImode, 6)));
+  if (excepts & fe_underflow)
+    emit_insn (gen_rs6000_mtfsb1 (gen_rtx_CONST_INT (SImode, 4)));
+  if (excepts & fe_overflow)
+    emit_insn (gen_rs6000_mtfsb1 (gen_rtx_CONST_INT (SImode, 3)));
+
+  emit_move_insn (operands[0], const0_rtx);
+  DONE;
+})
 
 ;; Define the TImode operations that can be done in a small number
 ;; of instructions.  The & constraints are to prevent the register
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 10d466fae9a4..fdd76579ce33 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -12858,6 +12858,9 @@  is called and the @var{flag} argument passed to it.
 @findex __builtin_alloca_with_align_and_max
 @findex __builtin_call_with_static_chain
 @findex __builtin_extend_pointer
+@findex __builtin_feclearexcept
+@findex __builtin_fegetround
+@findex __builtin_feraiseexcept
 @findex __builtin_fpclassify
 @findex __builtin_has_attribute
 @findex __builtin_isfinite
@@ -13424,6 +13427,16 @@  In the same fashion, GCC provides @code{fpclassify}, @code{isfinite},
 @code{__builtin_} prefixed.  The @code{isinf} and @code{isnan}
 built-in functions appear both with and without the @code{__builtin_} prefix.
 
+GCC provides built-in versions of the ISO C99 floating-point rounding and
+exceptions handling functions @code{fegetround}, @code{feclearexcept} and
+@code{feraiseexcept}. This built-in versions take extra arguments to describe
+the c library's notion of the possible rounding modes and exceptions values and
+are used internally to enable an transparent approach independent of the c
+library used.  We intend for a library implementer to be able to simply
+@code{#define} each function to its built-in equivalent with the extra
+arguments.  This built-in functions appear both with and without the
+@code{__builtin_} prefix.
+
 @deftypefn {Built-in Function} void *__builtin_alloca (size_t size)
 The @code{__builtin_alloca} function must be called at block scope.
 The function allocates an object @var{size} bytes large on the stack
@@ -14197,6 +14210,44 @@  Similar to @code{__builtin_huge_val}, except the return type is
 @code{_Float@var{n}x}.
 @end deftypefn
 
+@deftypefn {Built-in Function} int __builtin_fegetround (int, int, int, int)
+This built-in implements the C99 fegetround functionality.  The four int
+arguments should be the target library's notion of the possible FP rouding
+modes.  They must be constant values and they must appear in this order:
+@code{FE_DOWNWARD}, @code{FE_TONEAREST}, @code{FE_TOWARDZERO},
+@code{FE_UPWARD}.  In other words:
+
+@smallexample
+__builtin_fegetround(FE_DOWNWARD, FE_TONEAREST,
+                     FE_TOWARDZERO, FE_UPWARD)
+@end smallexample
+
+This values are used to match the processor values for the rounding
+mode with the target library's.
+@end deftypefn
+
+@deftypefn {Built-in Function} int __builtin_feclearexcept (int, int, int, int, int, int)
+This built-in implements the C99 feclearexcept functionality.  The first
+argument is the original feclearexcept() EXCEPTS argument.  The other five int
+arguments should be the target library's notion of the possible FP exceptions.
+They must be constant values and they must appear in this order:
+@code{FE_DIVBYZERO}, @code{FE_INEXACT}, @code{FE_INVALID}, @code{FE_OVERFLOW},
+@code{FE_UNDERFLOW}.  In other words:
+
+@smallexample
+__builtin_feclearexcept(excepts, FE_DIVBYZERO, FE_INEXACT,
+                        FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW)
+@end smallexample
+
+This values are used to match the processor values for the exception with the
+target library's.
+@end deftypefn
+
+@deftypefn {Built-in Function} int __builtin_feraiseexcept (int, int, int, int, int, int)
+Analogous to @code{__builtin_feclearexcept}, except to raise excepts instead of
+clearing and all arguments have the same semantics.
+@end deftypefn
+
 @deftypefn {Built-in Function} int __builtin_fpclassify (int, int, int, int, int, ...)
 This built-in implements the C99 fpclassify functionality.  The first
 five int arguments should be the target library's notion of the
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 41f1850bf6e9..9b39a09f1892 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -6053,6 +6053,27 @@  mode @var{m}, which is a scalar or vector floating-point mode.
 
 This pattern is not allowed to @code{FAIL}.
 
+@cindex @code{fegetround@var{m}} instruction pattern
+@item @samp{fegetround@var{m}}
+Store the current machine floating-point rounding mode into operand 0.
+Libc values for FE_DOWNWARD, FE_TONEAREST, FE_TOWARDZERO and FE_UPWARD
+are passed to operands 1, 2, 3 and 4 respectively.  All operands have
+mode @var{m}, which is scalar.  This pattern is used to implement the
+@code{fegetround} function from the ISO C99 standard.
+
+@cindex @code{feclearexcept@var{m}} instruction pattern
+@cindex @code{feraiseexcept@var{m}} instruction pattern
+@item @samp{feclearexcept@var{m}}
+@item @samp{feraiseexcept@var{m}}
+Clears or raises the supported machine floating-point exceptions
+represented by the bits in operand 1.  Libc values for FE_DIVBYZERO,
+FE_INEXACT, FE_INVALID, FE_OVERFLOW and FE_UNDERFLOW are passed to
+operands 2, 3, 4, 5 and 6 respectively.  Error status is stored as
+nonzero value in operand 0.  Both operands have mode @var{m}, which is
+a scalar.  These patterns are used to implement the
+@code{feclearexcept} and @code{feraiseexcept} functions from the ISO
+C99 standard.
+
 @cindex @code{exp@var{m}2} instruction pattern
 @item @samp{exp@var{m}2}
 Raise e (the base of natural logarithms) to the power of operand 1
diff --git a/gcc/optabs.def b/gcc/optabs.def
index f02c7b729a51..49f8d33bcf0d 100644
--- a/gcc/optabs.def
+++ b/gcc/optabs.def
@@ -327,6 +327,10 @@  OPTAB_D (sinh_optab, "sinh$a2")
 OPTAB_D (tan_optab, "tan$a2")
 OPTAB_D (tanh_optab, "tanh$a2")
 
+OPTAB_D (fegetround_optab, "fegetround$a")
+OPTAB_D (feclearexcept_optab, "feclearexcept$a")
+OPTAB_D (feraiseexcept_optab, "feraiseexcept$a")
+
 /* C99 implementations of fmax/fmin.  */
 OPTAB_D (fmax_optab, "fmax$a3")
 OPTAB_D (fmin_optab, "fmin$a3")
diff --git a/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-1.c b/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-1.c
new file mode 100644
index 000000000000..f8c3b072fe3f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-1.c
@@ -0,0 +1,82 @@ 
+/* { dg-do run } */
+/* { dg-require-effective-target fenv_exceptions } */
+/* { dg-options "-lm -fno-builtin" } */
+
+/* This testcase ensures that the builtins expand with the matching arguments
+ * or otherwise fallback gracefully to a function call, and don't ICE during
+ * compilation.
+ * "-fno-builtin" option is used to enable calls to libc implementation of the
+ * gcc builtins tested when not using __builtin_ prefix. */
+
+#include <fenv.h>
+
+#define __builtin_feraiseexcept(x) \
+        __builtin_feraiseexcept(x, FE_DIVBYZERO, FE_INEXACT, FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW)
+
+#define __builtin_feclearexcept(x) \
+        __builtin_feclearexcept(x, FE_DIVBYZERO, FE_INEXACT, FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW)
+
+int
+main ()
+{
+  int   rsi = 0;
+  long  rsl = 0;
+  short rss = 0;
+  char  rsc = 0;
+
+  unsigned int   rui = 0;
+  unsigned long  rul = 0;
+  unsigned short rus = 0;
+  unsigned char  ruc = 0;
+
+  int e = FE_DIVBYZERO;
+
+  __builtin_feclearexcept(e);                          // CALL
+  __builtin_feclearexcept(FE_ALL_EXCEPT);              // CALL
+  __builtin_feclearexcept(FE_INVALID);                 // CALL
+  __builtin_feclearexcept(FE_INVALID | FE_INEXACT);    // CALL
+
+  __builtin_feclearexcept(FE_INEXACT | FE_DIVBYZERO |
+                          FE_UNDERFLOW | FE_OVERFLOW);  // EXPAND
+  __builtin_feclearexcept(FE_INEXACT | FE_OVERFLOW);    // EXPAND
+  __builtin_feclearexcept(FE_INEXACT);                  // EXPAND
+  __builtin_feclearexcept(FE_DIVBYZERO);                // EXPAND
+  __builtin_feclearexcept(FE_UNDERFLOW);                // EXPAND
+  __builtin_feclearexcept(FE_OVERFLOW);                 // EXPAND
+  __builtin_feclearexcept(0);                           // EXPAND
+
+  rsi = __builtin_feclearexcept(FE_DIVBYZERO);  // EXPAND
+  rsl = __builtin_feclearexcept(FE_DIVBYZERO);  // EXPAND
+  rss = __builtin_feclearexcept(FE_DIVBYZERO);  // EXPAND
+  rsc = __builtin_feclearexcept(FE_DIVBYZERO);  // EXPAND
+  rui = __builtin_feclearexcept(FE_DIVBYZERO);  // EXPAND
+  rul = __builtin_feclearexcept(FE_DIVBYZERO);  // EXPAND
+  rus = __builtin_feclearexcept(FE_DIVBYZERO);  // EXPAND
+  ruc = __builtin_feclearexcept(FE_DIVBYZERO);  // EXPAND
+
+
+  __builtin_feraiseexcept(e);                          // CALL
+  __builtin_feraiseexcept(FE_ALL_EXCEPT);              // CALL
+  __builtin_feraiseexcept(FE_INVALID);                 // CALL
+  __builtin_feraiseexcept(FE_INVALID | FE_INEXACT);    // CALL
+
+  __builtin_feraiseexcept(FE_INEXACT | FE_DIVBYZERO |
+                          FE_UNDERFLOW | FE_OVERFLOW);  // EXPAND
+  __builtin_feraiseexcept(FE_INEXACT | FE_OVERFLOW);    // EXPAND
+  __builtin_feraiseexcept(FE_INEXACT);                  // EXPAND
+  __builtin_feraiseexcept(FE_DIVBYZERO);                // EXPAND
+  __builtin_feraiseexcept(FE_UNDERFLOW);                // EXPAND
+  __builtin_feraiseexcept(FE_OVERFLOW);                 // EXPAND
+  __builtin_feraiseexcept(0);                           // EXPAND
+
+  rsi = __builtin_feraiseexcept(FE_DIVBYZERO);  // EXPAND
+  rsl = __builtin_feraiseexcept(FE_DIVBYZERO);  // EXPAND
+  rss = __builtin_feraiseexcept(FE_DIVBYZERO);  // EXPAND
+  rsc = __builtin_feraiseexcept(FE_DIVBYZERO);  // EXPAND
+  rui = __builtin_feraiseexcept(FE_DIVBYZERO);  // EXPAND
+  rul = __builtin_feraiseexcept(FE_DIVBYZERO);  // EXPAND
+  rus = __builtin_feraiseexcept(FE_DIVBYZERO);  // EXPAND
+  ruc = __builtin_feraiseexcept(FE_DIVBYZERO);  // EXPAND
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-2.c b/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-2.c
new file mode 100644
index 000000000000..397396623532
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-2.c
@@ -0,0 +1,209 @@ 
+/* { dg-do run } */
+/* { dg-require-effective-target fenv_exceptions } */
+/* { dg-options "-lm -fno-builtin" } */
+
+/* This testcase ensures that the builtins are correctly expanded and match the
+ * expected result.
+ * "-fno-builtin" option is used to enable calls to libc implementation of the
+ * gcc builtins tested when not using __builtin_ prefix. */
+
+#include <fenv.h>
+
+#define __builtin_feraiseexcept(x) \
+        __builtin_feraiseexcept(x, FE_DIVBYZERO, FE_INEXACT, FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW)
+
+#define __builtin_feclearexcept(x) \
+        __builtin_feclearexcept(x, FE_DIVBYZERO, FE_INEXACT, FE_INVALID, FE_OVERFLOW, FE_UNDERFLOW)
+
+#ifdef DEBUG
+#include <stdio.h>
+#define INFO(...) printf(__VA_ARGS__)
+#define FAIL(v, e, x, s, f) \
+        printf("ERROR [l %d] testing %s(%x): %s returned %x," \
+               " expecected %x\n", __LINE__, s, x, f, v, e)
+#else
+void abort (void);
+#define INFO(...)
+#define FAIL(v, e, x, s, f) abort()
+#endif
+
+int
+main ()
+{
+  char *s = 0;
+  int e = 0;
+  int raised = 0;
+
+  s = "FE_ALL_EXCEPT";
+  e = FE_ALL_EXCEPT;
+  INFO("test: %s(%x)\n", s, e);
+
+  feclearexcept(FE_ALL_EXCEPT);
+  __builtin_feraiseexcept(FE_ALL_EXCEPT);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != e)
+    FAIL(raised, e, e, s, "__builtin_feraiseexcept");
+
+  feraiseexcept(FE_ALL_EXCEPT);
+  __builtin_feclearexcept(FE_ALL_EXCEPT);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != (FE_ALL_EXCEPT & ~e))
+    FAIL(raised, FE_ALL_EXCEPT & ~e, e, s, "__builtin_feclearexcept");
+
+
+  s = "NONE";
+  e = 0;
+  INFO("test: %s(%x)\n", s, e);
+
+  feclearexcept(FE_ALL_EXCEPT);
+  __builtin_feraiseexcept(0);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != e)
+    FAIL(raised, e, e, s, "__builtin_feraiseexcept");
+
+  feraiseexcept(FE_ALL_EXCEPT);
+  __builtin_feclearexcept(0);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != (FE_ALL_EXCEPT & ~e))
+    FAIL(raised, FE_ALL_EXCEPT & ~e, e, s, "__builtin_feclearexcept");
+
+
+  s = "FE_DIVBYZERO";
+  e = FE_DIVBYZERO;
+  INFO("test: %s(%x)\n", s, e);
+
+  feclearexcept(FE_ALL_EXCEPT);
+  __builtin_feraiseexcept(FE_DIVBYZERO);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != e)
+    FAIL(raised, e, e, s, "__builtin_feraiseexcept");
+
+  feraiseexcept(FE_ALL_EXCEPT);
+  __builtin_feclearexcept(FE_DIVBYZERO);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != (FE_ALL_EXCEPT & ~e))
+    FAIL(raised, FE_ALL_EXCEPT & ~e, e, s, "__builtin_feclearexcept");
+
+
+  s = "FE_INEXACT";
+  e = FE_INEXACT;
+  INFO("test: %s(%x)\n", s, e);
+
+  feclearexcept(FE_ALL_EXCEPT);
+  __builtin_feraiseexcept(FE_INEXACT);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != e)
+    FAIL(raised, e, e, s, "__builtin_feraiseexcept");
+
+  feraiseexcept(FE_ALL_EXCEPT);
+  __builtin_feclearexcept(FE_INEXACT);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != (FE_ALL_EXCEPT & ~e))
+    FAIL(raised, FE_ALL_EXCEPT & ~e, e, s, "__builtin_feclearexcept");
+
+
+  s = "FE_OVERFLOW";
+  e = FE_OVERFLOW;
+  INFO("test: %s(%x)\n", s, e);
+
+  feclearexcept(FE_ALL_EXCEPT);
+  __builtin_feraiseexcept(FE_OVERFLOW);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != e)
+    FAIL(raised, e, e, s, "__builtin_feraiseexcept");
+
+  feraiseexcept(FE_ALL_EXCEPT);
+  __builtin_feclearexcept(FE_OVERFLOW);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != (FE_ALL_EXCEPT & ~e))
+    FAIL(raised, FE_ALL_EXCEPT & ~e, e, s, "__builtin_feclearexcept");
+
+
+  s = "FE_UNDERFLOW";
+  e = FE_UNDERFLOW;
+  INFO("test: %s(%x)\n", s, e);
+
+  feclearexcept(FE_ALL_EXCEPT);
+  __builtin_feraiseexcept(FE_UNDERFLOW);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != e)
+    FAIL(raised, e, e, s, "__builtin_feraiseexcept");
+
+  feraiseexcept(FE_ALL_EXCEPT);
+  __builtin_feclearexcept(FE_UNDERFLOW);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != (FE_ALL_EXCEPT & ~e))
+    FAIL(raised, FE_ALL_EXCEPT & ~e, e, s, "__builtin_feclearexcept");
+
+
+  s = "FE_INVALID";
+  e = FE_INVALID;
+  INFO("test: %s(%x)\n", s, e);
+
+  feclearexcept(FE_ALL_EXCEPT);
+  __builtin_feraiseexcept(FE_INVALID);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != e)
+    FAIL(raised, e, e, s, "__builtin_feraiseexcept");
+
+  feraiseexcept(FE_ALL_EXCEPT);
+  __builtin_feclearexcept(FE_INVALID);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != (FE_ALL_EXCEPT & ~e))
+    FAIL(raised, FE_ALL_EXCEPT & ~e, e, s, "__builtin_feclearexcept");
+
+
+  s = "FE_INVALID | FE_INEXACT";
+  e = FE_INVALID | FE_INEXACT;
+  INFO("test: %s(%x)\n", s, e);
+
+  feclearexcept(FE_ALL_EXCEPT);
+  __builtin_feraiseexcept(FE_INVALID | FE_INEXACT);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != e)
+    FAIL(raised, e, e, s, "__builtin_feraiseexcept");
+
+  feraiseexcept(FE_ALL_EXCEPT);
+  __builtin_feclearexcept(FE_INVALID | FE_INEXACT);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != (FE_ALL_EXCEPT & ~e))
+    FAIL(raised, FE_ALL_EXCEPT & ~e, e, s, "__builtin_feclearexcept");
+
+
+  s = "FE_INEXACT | FE_OVERFLOW";
+  e = FE_INEXACT | FE_OVERFLOW;
+  INFO("test: %s(%x)\n", s, e);
+
+  feclearexcept(FE_ALL_EXCEPT);
+  __builtin_feraiseexcept(FE_INEXACT | FE_OVERFLOW);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != e)
+    FAIL(raised, e, e, s, "__builtin_feraiseexcept");
+
+  feraiseexcept(FE_ALL_EXCEPT);
+  __builtin_feclearexcept(FE_INEXACT | FE_OVERFLOW);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != (FE_ALL_EXCEPT & ~e))
+    FAIL(raised, FE_ALL_EXCEPT & ~e, e, s, "__builtin_feclearexcept");
+
+
+  s = "FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW";
+  e = FE_INEXACT | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW;
+  INFO("test: %s(%x)\n", s, e);
+
+  feclearexcept(FE_ALL_EXCEPT);
+  __builtin_feraiseexcept(FE_INEXACT | FE_DIVBYZERO |
+                          FE_UNDERFLOW | FE_OVERFLOW);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != e)
+    FAIL(raised, e, e, s, "__builtin_feraiseexcept");
+
+  feraiseexcept(FE_ALL_EXCEPT);
+  __builtin_feclearexcept(FE_INEXACT | FE_DIVBYZERO |
+                          FE_UNDERFLOW | FE_OVERFLOW);
+  raised = fetestexcept(FE_ALL_EXCEPT);
+  if (raised != (FE_ALL_EXCEPT & ~e))
+    FAIL(raised, FE_ALL_EXCEPT & ~e, e, s, "__builtin_feclearexcept");
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/powerpc/builtin-fegetround.c b/gcc/testsuite/gcc.target/powerpc/builtin-fegetround.c
new file mode 100644
index 000000000000..94238af6c287
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/builtin-fegetround.c
@@ -0,0 +1,39 @@ 
+/* { dg-do run } */
+/* { dg-require-effective-target fenv_exceptions } */
+/* { dg-options "-lm -fno-builtin" } */
+
+/* This testcase ensures that the builtins is correctly expanded and match the
+ * expected result from the standard function.
+ * "-fno-builtin" option is used to enable calls to libc implementation of the
+ * gcc builtins tested when not using __builtin_ prefix. */
+
+#include <fenv.h>
+
+#define __builtin_fegetround(x) \
+        __builtin_fegetround(FE_DOWNWARD, FE_TONEAREST, FE_TOWARDZERO, FE_UPWARD)
+
+#ifdef DEBUG
+#include <stdio.h>
+#define FAIL(v, e) printf("ERROR, __builtin_fegetround() returned %d," \
+                          " not the expecected value %d\n", v, e);
+#else
+void abort (void);
+#define FAIL(v, e) abort()
+#endif
+
+int
+main ()
+{
+  int i, rounding, expected;
+  const int rm[] = {FE_TONEAREST, FE_TOWARDZERO, FE_UPWARD, FE_DOWNWARD};
+  for (i = 0; i < sizeof(rm); i++)
+    {
+      fesetround(rm[i]);
+      rounding = __builtin_fegetround();
+      expected = fegetround();
+      if (rounding != expected)
+        FAIL(rounding, expected);
+    }
+
+  return 0;
+}