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

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

Commit Message

Raoni Fassina Firmino Nov. 24, 2021, 11:48 p.m. UTC
  Changes since v6[6] and v5[5]:
  - Based this version on the v5 one.
  - Reworked all builtins back to the way they are in v5 and added the
    following changes:
    + Added a test to target libc, only expanding with glibc as the
      target libc.
    + Updated all three expanders header comment to reflect the added
      behavior (fegetround got a full header as it had none).
    + Added extra documentation for the builtins on doc/extend.texi,
      similar to v6 version, but only the introductory paragraph,
      without a dedicated entry for each, since now they behavior and
      signature match the C99 ones.
  - Changed the description for the return operand in the RTL template
    of the fegetround expander.  Using "(set )", the same way as
    rs6000_mffsl expander (this change was taken from v6).
  - Updated the commit message mentioning the target libc restriction
    and updated changelog.

Tested on top of master (9bf69a8558638ce0cdd69e83a68776deb9b8e053)
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)

Also made a visual test comparing the generated assembly of a test
program built against glibc and musl (with -mmusl and with musl-gcc).

Documentation changes tested on x86_64-redhat-linux.

Well, turns out v6 was kind of a misstep[7].  But turns out the
solution was in my face the whole time and Joseph was kind enough to
spell it out to me.  I should have known, one can check for the target
libc at runtime. It is a really simple addition to each expander, only
expanding for the libcs the expander know the FE_* and can handle it.
As Joseph mentioned on his review, with that the expander don't have
to always expand and everything is fine.

As I mentioned[8], musl and uclibc both uses the same values as glibc,
I could add then enabling the expanders for them, not sure about it.

I don't know if I should add something to the documentation, more
precisely on section "6.59 Other Built-in Functions Provided by GCC"
in doc/extend.text. Like I mentioned in v6 but I don't know if I'm
doing it right, especially changing such a front facing documentation,
but here it is.

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

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.

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 counterpart;
  - 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 suggested 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/pipermail/gcc-patches/2021-October/581837.html
[7] https://gcc.gnu.org/pipermail/gcc-patches/2021-October/581929.html
[8] https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558070.html

---- 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.

The builtin expanders needs knowledge of the target libc's FE_*
values, so they are limited to expand only to suitable libcs.

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:

        * 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
        * 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.
        * 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/builtins.c                                |  76 +++++++
 gcc/config/rs6000/rs6000.md                   | 111 ++++++++++
 gcc/doc/extend.texi                           |   8 +
 gcc/doc/md.texi                               |  17 ++
 gcc/optabs.def                                |   4 +
 .../builtin-feclearexcept-feraiseexcept-1.c   |  76 +++++++
 .../builtin-feclearexcept-feraiseexcept-2.c   | 203 ++++++++++++++++++
 .../gcc.target/powerpc/builtin-fegetround.c   |  36 ++++
 8 files changed, 531 insertions(+)
 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

Segher Boessenkool Nov. 25, 2021, 9:12 p.m. UTC | #1
Hi!

On Wed, Nov 24, 2021 at 08:48:47PM -0300, Raoni Fassina Firmino wrote:
> gcc/ChangeLog:
>         * 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

Something is missing here (maybe just a full stop?)

>         * 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.

Pet peeve: please don't break lines early, we have only 72 columns per
line and we have many long symbol names.  Trying to make many lines very
short only results in everything looking very irregular, which is harder
to read.

>         * 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.

> --- a/gcc/config/rs6000/rs6000.md
> +++ b/gcc/config/rs6000/rs6000.md
> @@ -6860,6 +6860,117 @@
>    [(set_attr "type" "fpload")
>     (set_attr "length" "8")
>     (set_attr "isa" "*,p8v,p8v")])
> +
> +;; int fegetround(void)
> +;;
> +;; This expansion for the C99 function only expands for compatible
> +;; target libcs. Because it needs to return one of FE_DOWNWARD,
> +;; FE_TONEAREST, FE_TOWARDZERO or FE_UPWARD with the values as defined
> +;; by the target libc, and since they are free to
> +;; choose the values and the expand needs to know then beforehand,
> +;; this expand only expands for target libcs that it can handle the
> +;; values is knows.
> +;; Because of these restriction, this only expands on the desired
> +;; case and fallback to a call to libc on any otherwise.
> +(define_expand "fegetroundsi"

(This needs some wordsmithing.)

> +;; int feclearexcept(int excepts)
> +;;
> +;; This expansion for the C99 function only works when EXCEPTS 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.

It FAILs the expansion if a parameter is bad?  Is this comment out of
date?

> +;; Note that FE_INVALID is unsupported because it maps to more than
> +;; one bit of the FPSCR register.

It could be implemented, now that you check for the libc used.  It is a
fixed part of the ABI :-)

> +;; The FE_* are defined in the targed libc, and since they are free to
> +;; choose the values and the expand needs to know then beforehand,

s/then/them/

> +;; this expand only expands for target libcs that it can handle the

(this expander)

> +;; values is knows.

s/is/it/

> +/* 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. */

Don't use leading * in comments, btw.  This is a testcase so anything
goes, but FYI :-)

> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/powerpc/builtin-fegetround.c

> +  int i, rounding, expected;
> +  const int rm[] = {FE_TONEAREST, FE_TOWARDZERO, FE_UPWARD, FE_DOWNWARD};
> +  for (i = 0; i < sizeof(rm); i++)

That should be   sizeof rm / sizeof rm[0]   ?  It accesses out of bounds
as it is.

Maybe test more values?  At least 0, but also combinations of these FE_
bits, and maybe even FE_INVALID?

With such changes the rs6000 parts are okay for trunk.  Thanks!

I looked at the generic changes as well, and they all look fine to me.


Segher
  
Jeff Law Dec. 15, 2021, 8:29 p.m. UTC | #2
On 11/24/2021 4:48 PM, Raoni Fassina Firmino via Gcc-patches wrote:
> Changes since v6[6] and v5[5]:
>    - Based this version on the v5 one.
>    - Reworked all builtins back to the way they are in v5 and added the
>      following changes:
>      + Added a test to target libc, only expanding with glibc as the
>        target libc.
>      + Updated all three expanders header comment to reflect the added
>        behavior (fegetround got a full header as it had none).
>      + Added extra documentation for the builtins on doc/extend.texi,
>        similar to v6 version, but only the introductory paragraph,
>        without a dedicated entry for each, since now they behavior and
>        signature match the C99 ones.
>    - Changed the description for the return operand in the RTL template
>      of the fegetround expander.  Using "(set )", the same way as
>      rs6000_mffsl expander (this change was taken from v6).
>    - Updated the commit message mentioning the target libc restriction
>      and updated changelog.
>
> Tested on top of master (9bf69a8558638ce0cdd69e83a68776deb9b8e053)
> 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)
>
> Also made a visual test comparing the generated assembly of a test
> program built against glibc and musl (with -mmusl and with musl-gcc).
>
> Documentation changes tested on x86_64-redhat-linux.
>
> Well, turns out v6 was kind of a misstep[7].  But turns out the
> solution was in my face the whole time and Joseph was kind enough to
> spell it out to me.  I should have known, one can check for the target
> libc at runtime. It is a really simple addition to each expander, only
> expanding for the libcs the expander know the FE_* and can handle it.
> As Joseph mentioned on his review, with that the expander don't have
> to always expand and everything is fine.
>
> As I mentioned[8], musl and uclibc both uses the same values as glibc,
> I could add then enabling the expanders for them, not sure about it.
>
> I don't know if I should add something to the documentation, more
> precisely on section "6.59 Other Built-in Functions Provided by GCC"
> in doc/extend.text. Like I mentioned in v6 but I don't know if I'm
> doing it right, especially changing such a front facing documentation,
> but here it is.
>
> I'm repeating the "changelog" from past versions here for convenience:
>
> 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.
>
> 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 counterpart;
>    - 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 suggested 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/pipermail/gcc-patches/2021-October/581837.html
> [7] https://gcc.gnu.org/pipermail/gcc-patches/2021-October/581929.html
> [8] https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558070.html
>
> ---- 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.
>
> The builtin expanders needs knowledge of the target libc's FE_*
> values, so they are limited to expand only to suitable libcs.
>
> 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:
>
>          * 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
>          * 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.
>          * 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.
I think the generic parts are fine once Segher is happy with the rest of 
the bits.

jeff
  
Segher Boessenkool Dec. 16, 2021, 1:01 a.m. UTC | #3
On Wed, Dec 15, 2021 at 01:29:57PM -0700, Jeff Law wrote:
> I think the generic parts are fine once Segher is happy with the rest of 
> the bits.

https://gcc.gnu.org/pipermail/gcc-patches/2021-November/585478.html
(last lines)

:-)


Segher
  
Raoni Fassina Firmino Dec. 23, 2021, 5:16 p.m. UTC | #4
Hi Segher,

On Thu, Nov 25, 2021 at 03:12:32PM -0600, Segher Boessenkool wrote:
> Hi!
> 
> On Wed, Nov 24, 2021 at 08:48:47PM -0300, Raoni Fassina Firmino wrote:
> > gcc/ChangeLog:
> >         * 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
> 
> Something is missing here (maybe just a full stop?)

Yeap. Done.


> >         * 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.
> 
> Pet peeve: please don't break lines early, we have only 72 columns per
> line and we have many long symbol names.  Trying to make many lines very
> short only results in everything looking very irregular, which is harder
> to read.

Sure thing, it is my bad that I have shortcuts for 70 and 80 textwidth
but not 72.

In any case:

|        * doc/extend.texi: Add a new introductory paragraph about the new
|        builtins.

It would be 73 columns or I am reading my text editor wrong?
Also here:

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

It is 79 for now, but has the same 73 problem, I guess the correct
formatting is:

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

is that right?


> > +;; int fegetround(void)
> > +;;
> > +;; This expansion for the C99 function only expands for compatible
> > +;; target libcs. Because it needs to return one of FE_DOWNWARD,
> > +;; FE_TONEAREST, FE_TOWARDZERO or FE_UPWARD with the values as defined
> > +;; by the target libc, and since they are free to
> > +;; choose the values and the expand needs to know then beforehand,
> > +;; this expand only expands for target libcs that it can handle the
> > +;; values is knows.
> > +;; Because of these restriction, this only expands on the desired
> > +;; case and fallback to a call to libc on any otherwise.
> > +(define_expand "fegetroundsi"
> 
> (This needs some wordsmithing.)



> > +;; int feclearexcept(int excepts)
> > +;;
> > +;; This expansion for the C99 function only works when EXCEPTS 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.
> 
> It FAILs the expansion if a parameter is bad?  Is this comment out of
> date?

If the parameter is one that it cannot handle, including boggus values,
it then FAILs and the libc function will handle it, including returning
error for wrong input.

This part is verbatin from v5 (priour to the refactoring that then was
undone)


> > +;; Note that FE_INVALID is unsupported because it maps to more than
> > +;; one bit of the FPSCR register.
> 
> It could be implemented, now that you check for the libc used.  It is a
> fixed part of the ABI :-)

Oh yeah, I can add it now or in a subsequent commit, is that a hard
requirement for the patch?


> > +;; The FE_* are defined in the targed libc, and since they are free to
> > +;; choose the values and the expand needs to know then beforehand,
> 
> s/then/them/

Done.


> > +;; this expand only expands for target libcs that it can handle the
> 
> (this expander)

Done.


> > +;; values is knows.
> 
> s/is/it/

Done.


> > +/* 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. */
> 
> Don't use leading * in comments, btw.

Done.

> This is a testcase so anything
> goes, but FYI :-)

Yeah, better keep the same style :-)


> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/powerpc/builtin-fegetround.c
> 
> > +  int i, rounding, expected;
> > +  const int rm[] = {FE_TONEAREST, FE_TOWARDZERO, FE_UPWARD, FE_DOWNWARD};
> > +  for (i = 0; i < sizeof(rm); i++)
> 
> That should be   sizeof rm / sizeof rm[0]   ?  It accesses out of bounds
> as it is.

Done. Thanks for the catch, newbie mistake on my part.


> Maybe test more values?  At least 0, but also combinations of these FE_
> bits, and maybe even FE_INVALID?

I Don't get what you mean, like use some invalid values for
fesetround()? I am using only expected values because fegetround() will
only read what was previously set. I could set some invalid values and
expect that it did not change the value expected to be read in
fegetround but then I would be testing fesetround and not fegetround I
guess.


> 
> With such changes the rs6000 parts are okay for trunk.  Thanks!
> 
> I looked at the generic changes as well, and they all look fine to me.
> 
> 
> Segher

Thanks :)


o/
Raoni
  
Raoni Fassina Firmino Dec. 23, 2021, 5:30 p.m. UTC | #5
Sorry, sent an incomplete email. it was missing this part:

On Thu, Nov 25, 2021 at 03:12:32PM -0600, Segher Boessenkool wrote:
> > +;; int fegetround(void)
> > +;;
> > +;; This expansion for the C99 function only expands for compatible
> > +;; target libcs. Because it needs to return one of FE_DOWNWARD,
> > +;; FE_TONEAREST, FE_TOWARDZERO or FE_UPWARD with the values as defined
> > +;; by the target libc, and since they are free to
> > +;; choose the values and the expand needs to know then beforehand,
> > +;; this expand only expands for target libcs that it can handle the
> > +;; values is knows.
> > +;; Because of these restriction, this only expands on the desired
> > +;; case and fallback to a call to libc on any otherwise.
> > +(define_expand "fegetroundsi"
> 
> (This needs some wordsmithing.)

How about something like this? It is just a light editing of the above:

;; This expansion for the C99 function only expands for compatible
;; target libcs, because it needs to return one of FE_DOWNWARD,
;; FE_TONEAREST, FE_TOWARDZERO or FE_UPWARD with the values as defined
;; by the target libc, and since the libc is free to choose the values
;; (and they may differ from the hardware) and the expander needs to
;; know then beforehand, this expanded only expands for target libcs
;; that it can handle the values is knows.
;; Because of these restriction, this only expands on the desired
;; case and fallback to a call to libc otherwise.


o/
Raoni
  

Patch

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 384864bfb3a4..40690b133ebd 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,59 @@  expand_builtin_sincos (tree exp)
   return const0_rtx;
 }
 
+/* Expand call EXP to the fegetround builtin (from C99 fenv.h), returning the
+   result and setting it in TARGET.  Otherwise return NULL_RTX on failure.  */
+static rtx
+expand_builtin_fegetround (tree exp, rtx target, machine_mode target_mode)
+{
+  if (!validate_arglist (exp, 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 pat = GEN_FCN (icode) (target);
+  if (!pat)
+    return NULL_RTX;
+  emit_insn (pat);
+
+  return target;
+}
+
+/* Expand call EXP to either feclearexcept or feraiseexcept builtins (from C99
+   fenv.h), returning the result and setting it in TARGET.  Otherwise return
+   NULL_RTX on failure.  */
+static rtx
+expand_builtin_feclear_feraise_except (tree exp, rtx target,
+				       machine_mode target_mode, optab op_optab)
+{
+  if (!validate_arglist (exp, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+  rtx op0 = expand_normal (CALL_EXPR_ARG (exp, 0));
+
+  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 pat = GEN_FCN (icode) (target, op0);
+  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.  */
@@ -7006,6 +7062,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/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index 6bec2bddbdee..1aae3e83d64c 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -6860,6 +6860,117 @@ 
   [(set_attr "type" "fpload")
    (set_attr "length" "8")
    (set_attr "isa" "*,p8v,p8v")])
+
+;; int fegetround(void)
+;;
+;; This expansion for the C99 function only expands for compatible
+;; target libcs. Because it needs to return one of FE_DOWNWARD,
+;; FE_TONEAREST, FE_TOWARDZERO or FE_UPWARD with the values as defined
+;; by the target libc, and since they are free to
+;; choose the values and the expand needs to know then beforehand,
+;; this expand only expands for target libcs that it can handle the
+;; values is knows.
+;; Because of these restriction, this only expands on the desired
+;; case and fallback to a call to libc on any otherwise.
+(define_expand "fegetroundsi"
+  [(set (match_operand:SI 0 "gpc_reg_operand")
+	(unspec_volatile:SI [(const_int 0)] UNSPECV_MFFSL))]
+  "TARGET_HARD_FLOAT"
+{
+  if (!OPTION_GLIBC)
+    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 feclearexcept(int excepts)
+;;
+;; This expansion for the C99 function only works when EXCEPTS 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.
+;; The FE_* are defined in the targed libc, and since they are free to
+;; choose the values and the expand needs to know then beforehand,
+;; this expand only expands for target libcs that it can handle the
+;; values is knows.
+;; 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 1 "const_int_operand" "n"))
+   (set (match_operand:SI 0 "gpc_reg_operand")
+	(const_int 0))]
+  "TARGET_HARD_FLOAT"
+{
+  if (!OPTION_GLIBC)
+    FAIL;
+
+  unsigned int fe = INTVAL (operands[1]);
+  if (fe != (fe & 0x1e000000))
+    FAIL;
+
+  if (fe & 0x02000000)  /* FE_INEXACT */
+    emit_insn (gen_rs6000_mtfsb0 (gen_rtx_CONST_INT (SImode, 6)));
+  if (fe & 0x04000000)  /* FE_DIVBYZERO */
+    emit_insn (gen_rs6000_mtfsb0 (gen_rtx_CONST_INT (SImode, 5)));
+  if (fe & 0x08000000)  /* FE_UNDERFLOW */
+    emit_insn (gen_rs6000_mtfsb0 (gen_rtx_CONST_INT (SImode, 4)));
+  if (fe & 0x10000000)  /* FE_OVERFLOW */
+    emit_insn (gen_rs6000_mtfsb0 (gen_rtx_CONST_INT (SImode, 3)));
+
+  emit_move_insn (operands[0], const0_rtx);
+  DONE;
+})
+
+;; int feraiseexcept(int excepts)
+;;
+;; This expansion for the C99 function only works when excepts 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.
+;; The FE_* are defined in the targed libc, and since they are free to
+;; choose the values and the expand needs to know then beforehand,
+;; this expand only expands for target libcs that it can handle the
+;; values is knows.
+;; 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 1 "const_int_operand" "n"))
+   (set (match_operand:SI 0 "gpc_reg_operand")
+	(const_int 0))]
+  "TARGET_HARD_FLOAT"
+{
+  if (!OPTION_GLIBC)
+    FAIL;
+
+  unsigned int fe = INTVAL (operands[1]);
+  if (fe != (fe & 0x1e000000))
+    FAIL;
+
+  if (fe & 0x02000000)  /* FE_INEXACT */
+    emit_insn (gen_rs6000_mtfsb1 (gen_rtx_CONST_INT (SImode, 6)));
+  if (fe & 0x04000000)  /* FE_DIVBYZERO */
+    emit_insn (gen_rs6000_mtfsb1 (gen_rtx_CONST_INT (SImode, 5)));
+  if (fe & 0x08000000)  /* FE_UNDERFLOW */
+    emit_insn (gen_rs6000_mtfsb1 (gen_rtx_CONST_INT (SImode, 4)));
+  if (fe & 0x10000000)  /* 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 ef654d7b8788..92d3a6880c74 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -13426,6 +13426,14 @@  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}.  They may not be available for all targets, and because
+they need close interaction with libc internal values, they may not be available
+for all target libcs, but in all cases they will gracefully fallback to libc
+calls.  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
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 589f841ea741..0d4800694b16 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -6053,6 +6053,23 @@  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.
+Operand 0 has 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.  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 e25f4c9a3466..00620e18b7c2 100644
--- a/gcc/optabs.def
+++ b/gcc/optabs.def
@@ -330,6 +330,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..c9d5b5cefc0f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-1.c
@@ -0,0 +1,76 @@ 
+/* { 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>
+
+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..e1bf8091014b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-2.c
@@ -0,0 +1,203 @@ 
+/* { 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>
+
+#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..502ddf30ae2f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/builtin-fegetround.c
@@ -0,0 +1,36 @@ 
+/* { 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>
+
+#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;
+}