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

Message ID 20220113170853.mw2bcii7x2rgwc2l@workbox
State New
Headers
Series [v9] rtl: builtins: (not just) rs6000: Add builtins for fegetround, feclearexcept and feraiseexcept [PR94193] |

Commit Message

Raoni Fassina Firmino Jan. 13, 2022, 5:08 p.m. UTC
  Changes since v8[8]:
  - Refactored and expanded builtin-feclearexcept-feraiseexcept-2.c
    testcase:
    + Use a macro to avoid extended repetition of the core test code.
    + Expanded the test code to check builtins return code.
    + Added more tests to test all valid (standard) exceptions input
      combinations.
    + Updated the header comment to explain why the input must be
      passed as constants.

Changes since v7[7]:
  - Fixed an array indexing bug on fegeround testcase.
  - Fixed typos and spelling mistakes spread trouout the added comments.
  - Reworded header comment/description for fegetround expander.
  - Fixed changelog in the commit message.

This is an update to v8, based on the same review from Seguer to
expand the test coverage for feclearexcept and feraiseexcept (That is
also why I am keeping the v8 changelog here, since v8 had no reviews).
Two things to point out is: 1) The use of a macro there instead of a
function, unfortunately the builtins (for rs6000) only expand when the
input is a constant, so a macro is the way to go, and for the same
reason 2) I wanted to simplify the way to test all combinations of
input, but I could not think in a way without making some macro magics
that would be way less readable than listing all combinations by hand.

Tested on top of master (02a8a01bf396e009bfc31e1104c315fd403b4cca)
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.

==

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

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.

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-November/585375.html
[8] https://gcc.gnu.org/pipermail/gcc-patches/2021-December/587360.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   |  91 ++++++++++++++
 .../gcc.target/powerpc/builtin-fegetround.c   |  36 ++++++
 8 files changed, 419 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 Jan. 24, 2022, 2:55 p.m. UTC | #1
Hi!

On Thu, Jan 13, 2022 at 02:08:53PM -0300, Raoni Fassina Firmino wrote:
> Changes since v8[8]:
>   - Refactored and expanded builtin-feclearexcept-feraiseexcept-2.c
>     testcase:
>     + Use a macro to avoid extended repetition of the core test code.
>     + Expanded the test code to check builtins return code.
>     + Added more tests to test all valid (standard) exceptions input

This is okay for trunk (Jeff already approved the generic parts).
Thanks!


Segher
  
Jakub Jelinek Jan. 24, 2022, 8:26 p.m. UTC | #2
On Mon, Jan 24, 2022 at 08:55:37AM -0600, Segher Boessenkool wrote:
> Hi!
> 
> On Thu, Jan 13, 2022 at 02:08:53PM -0300, Raoni Fassina Firmino wrote:
> > Changes since v8[8]:
> >   - Refactored and expanded builtin-feclearexcept-feraiseexcept-2.c
> >     testcase:
> >     + Use a macro to avoid extended repetition of the core test code.
> >     + Expanded the test code to check builtins return code.
> >     + Added more tests to test all valid (standard) exceptions input
> 
> This is okay for trunk (Jeff already approved the generic parts).
> Thanks!

This breaks bootstrap with --enable-checking=rtl, e.g. while compiling
libquadmath/math/llrintq.c
#0  internal_error (gmsgid=0x131bb1e0 "RTL check: expected code '%s', have '%s' in %s, at %s:%d") at ../../gcc/diagnostic.cc:1938
#1  0x00000000113a0e94 in rtl_check_failed_code1 (r=0x3fffaf4a24a8, code=CONST_INT, file=0x13400018 "../../gcc/config/rs6000/rs6000.md", line=7010, 
    func=0x13409298 <gen_feraiseexceptsi(rtx_def*, rtx_def*)::__FUNCTION__> "gen_feraiseexceptsi") at ../../gcc/rtl.cc:918
#2  0x00000000125154e8 in gen_feraiseexceptsi (operand0=0x3fffaf4a3720, operand1=0x3fffaf4a24a8) at ../../gcc/config/rs6000/rs6000.md:7010
#3  0x00000000108badf4 in insn_gen_fn::operator()<rtx_def*, rtx_def*> (this=0x138ee440 <insn_data+131776>) at ../../gcc/recog.h:407
#4  0x0000000010890b1c in expand_builtin_feclear_feraise_except (exp=0x3fffaf3041a0, target=0x3fffaf4a3720, target_mode=E_SImode, op_optab=feraiseexcept_optab)
    at ../../gcc/builtins.cc:2606
#5  0x00000000108a6f74 in expand_builtin (exp=0x3fffaf3041a0, target=0x3fffaf100490, subtarget=0x0, mode=E_VOIDmode, ignore=1) at ../../gcc/builtins.cc:7130
#6  0x0000000010c01770 in expand_expr_real_1 (exp=0x3fffaf3041a0, target=0x0, tmode=E_VOIDmode, modifier=EXPAND_NORMAL, alt_rtl=0x0, inner_reference_p=false)
    at ../../gcc/expr.cc:11536
#7  0x0000000010bf0604 in expand_expr_real (exp=0x3fffaf3041a0, target=0x3fffaf100490, tmode=E_VOIDmode, modifier=EXPAND_NORMAL, alt_rtl=0x0, inner_reference_p=false)
    at ../../gcc/expr.cc:8737
#8  0x00000000108ffa00 in expand_expr (exp=0x3fffaf3041a0, target=0x3fffaf100490, mode=E_VOIDmode, modifier=EXPAND_NORMAL) at ../../gcc/expr.h:301
#9  0x000000001090c934 in expand_call_stmt (stmt=0x3fffaf1314d0) at ../../gcc/cfgexpand.cc:2831
#10 0x0000000010911e18 in expand_gimple_stmt_1 (stmt=0x3fffaf1314d0) at ../../gcc/cfgexpand.cc:3864
#11 0x0000000010912730 in expand_gimple_stmt (stmt=0x3fffaf1314d0) at ../../gcc/cfgexpand.cc:4028
#12 0x000000001091ecb0 in expand_gimple_basic_block (bb=0x3fffaf190c98, disable_tail_calls=false) at ../../gcc/cfgexpand.cc:6069
#13 0x0000000010921be8 in (anonymous namespace)::pass_expand::execute (this=0x13ab0d40, fun=0x3fffaf0c0c38) at ../../gcc/cfgexpand.cc:6795
#14 0x0000000011216ea4 in execute_one_pass (pass=0x13ab0d40) at ../../gcc/passes.cc:2637
#15 0x00000000112173d8 in execute_pass_list_1 (pass=0x13ab0d40) at ../../gcc/passes.cc:2737
#16 0x00000000112174b0 in execute_pass_list (fn=0x3fffaf0c0c38, pass=0x13aac8c0) at ../../gcc/passes.cc:2748
#17 0x00000000109b4e4c in cgraph_node::expand (this=0x3fffaf151760) at ../../gcc/cgraphunit.cc:1834
#18 0x00000000109b5844 in expand_all_functions () at ../../gcc/cgraphunit.cc:1998
#19 0x00000000109b67d0 in symbol_table::compile (this=0x3fffaf0d0000) at ../../gcc/cgraphunit.cc:2348
#20 0x00000000109b6f40 in symbol_table::finalize_compilation_unit (this=0x3fffaf0d0000) at ../../gcc/cgraphunit.cc:2529
#21 0x00000000114f10f4 in compile_file () at ../../gcc/toplev.cc:479
#22 0x00000000114f6204 in do_compile (no_backend=false) at ../../gcc/toplev.cc:2158
#23 0x00000000114f68d0 in toplev::main (this=0x3fffffffeb64, argc=45, argv=0x3fffffffef98) at ../../gcc/toplev.cc:2310
#24 0x0000000012f97a6c in main (argc=45, argv=0x3fffffffef98) at ../../gcc/main.cc:39

expand_builtin_feclear_feraise_except doesn't check if op0 matches
the predicate of operands[1], the backend requires const_int_operand,
but because the call isn't done with a constant integer:
feraiseexcept (t == LLONG_MIN ? FE_INEXACT : FE_INVALID);
op0 is a REG.
If CONST_INT is what is expected on all targets, then it should punt if
op0 isn't one, otherwise it should the predicate.

	Jakub
  
Pop, Sebastian via Gcc-patches Jan. 24, 2022, 8:29 p.m. UTC | #3
Adding the patch author for his information.

Thanks,
Bill

On 1/24/22 2:26 PM, Jakub Jelinek via Gcc-patches wrote:
> On Mon, Jan 24, 2022 at 08:55:37AM -0600, Segher Boessenkool wrote:
>> Hi!
>>
>> On Thu, Jan 13, 2022 at 02:08:53PM -0300, Raoni Fassina Firmino wrote:
>>> Changes since v8[8]:
>>>   - Refactored and expanded builtin-feclearexcept-feraiseexcept-2.c
>>>     testcase:
>>>     + Use a macro to avoid extended repetition of the core test code.
>>>     + Expanded the test code to check builtins return code.
>>>     + Added more tests to test all valid (standard) exceptions input
>> This is okay for trunk (Jeff already approved the generic parts).
>> Thanks!
> This breaks bootstrap with --enable-checking=rtl, e.g. while compiling
> libquadmath/math/llrintq.c
> #0  internal_error (gmsgid=0x131bb1e0 "RTL check: expected code '%s', have '%s' in %s, at %s:%d") at ../../gcc/diagnostic.cc:1938
> #1  0x00000000113a0e94 in rtl_check_failed_code1 (r=0x3fffaf4a24a8, code=CONST_INT, file=0x13400018 "../../gcc/config/rs6000/rs6000.md", line=7010, 
>     func=0x13409298 <gen_feraiseexceptsi(rtx_def*, rtx_def*)::__FUNCTION__> "gen_feraiseexceptsi") at ../../gcc/rtl.cc:918
> #2  0x00000000125154e8 in gen_feraiseexceptsi (operand0=0x3fffaf4a3720, operand1=0x3fffaf4a24a8) at ../../gcc/config/rs6000/rs6000.md:7010
> #3  0x00000000108badf4 in insn_gen_fn::operator()<rtx_def*, rtx_def*> (this=0x138ee440 <insn_data+131776>) at ../../gcc/recog.h:407
> #4  0x0000000010890b1c in expand_builtin_feclear_feraise_except (exp=0x3fffaf3041a0, target=0x3fffaf4a3720, target_mode=E_SImode, op_optab=feraiseexcept_optab)
>     at ../../gcc/builtins.cc:2606
> #5  0x00000000108a6f74 in expand_builtin (exp=0x3fffaf3041a0, target=0x3fffaf100490, subtarget=0x0, mode=E_VOIDmode, ignore=1) at ../../gcc/builtins.cc:7130
> #6  0x0000000010c01770 in expand_expr_real_1 (exp=0x3fffaf3041a0, target=0x0, tmode=E_VOIDmode, modifier=EXPAND_NORMAL, alt_rtl=0x0, inner_reference_p=false)
>     at ../../gcc/expr.cc:11536
> #7  0x0000000010bf0604 in expand_expr_real (exp=0x3fffaf3041a0, target=0x3fffaf100490, tmode=E_VOIDmode, modifier=EXPAND_NORMAL, alt_rtl=0x0, inner_reference_p=false)
>     at ../../gcc/expr.cc:8737
> #8  0x00000000108ffa00 in expand_expr (exp=0x3fffaf3041a0, target=0x3fffaf100490, mode=E_VOIDmode, modifier=EXPAND_NORMAL) at ../../gcc/expr.h:301
> #9  0x000000001090c934 in expand_call_stmt (stmt=0x3fffaf1314d0) at ../../gcc/cfgexpand.cc:2831
> #10 0x0000000010911e18 in expand_gimple_stmt_1 (stmt=0x3fffaf1314d0) at ../../gcc/cfgexpand.cc:3864
> #11 0x0000000010912730 in expand_gimple_stmt (stmt=0x3fffaf1314d0) at ../../gcc/cfgexpand.cc:4028
> #12 0x000000001091ecb0 in expand_gimple_basic_block (bb=0x3fffaf190c98, disable_tail_calls=false) at ../../gcc/cfgexpand.cc:6069
> #13 0x0000000010921be8 in (anonymous namespace)::pass_expand::execute (this=0x13ab0d40, fun=0x3fffaf0c0c38) at ../../gcc/cfgexpand.cc:6795
> #14 0x0000000011216ea4 in execute_one_pass (pass=0x13ab0d40) at ../../gcc/passes.cc:2637
> #15 0x00000000112173d8 in execute_pass_list_1 (pass=0x13ab0d40) at ../../gcc/passes.cc:2737
> #16 0x00000000112174b0 in execute_pass_list (fn=0x3fffaf0c0c38, pass=0x13aac8c0) at ../../gcc/passes.cc:2748
> #17 0x00000000109b4e4c in cgraph_node::expand (this=0x3fffaf151760) at ../../gcc/cgraphunit.cc:1834
> #18 0x00000000109b5844 in expand_all_functions () at ../../gcc/cgraphunit.cc:1998
> #19 0x00000000109b67d0 in symbol_table::compile (this=0x3fffaf0d0000) at ../../gcc/cgraphunit.cc:2348
> #20 0x00000000109b6f40 in symbol_table::finalize_compilation_unit (this=0x3fffaf0d0000) at ../../gcc/cgraphunit.cc:2529
> #21 0x00000000114f10f4 in compile_file () at ../../gcc/toplev.cc:479
> #22 0x00000000114f6204 in do_compile (no_backend=false) at ../../gcc/toplev.cc:2158
> #23 0x00000000114f68d0 in toplev::main (this=0x3fffffffeb64, argc=45, argv=0x3fffffffef98) at ../../gcc/toplev.cc:2310
> #24 0x0000000012f97a6c in main (argc=45, argv=0x3fffffffef98) at ../../gcc/main.cc:39
>
> expand_builtin_feclear_feraise_except doesn't check if op0 matches
> the predicate of operands[1], the backend requires const_int_operand,
> but because the call isn't done with a constant integer:
> feraiseexcept (t == LLONG_MIN ? FE_INEXACT : FE_INVALID);
> op0 is a REG.
> If CONST_INT is what is expected on all targets, then it should punt if
> op0 isn't one, otherwise it should the predicate.
>
> 	Jakub
>
  
Raoni Fassina Firmino Jan. 24, 2022, 9:24 p.m. UTC | #4
On Mon, Jan 24, 2022 at 02:29:39PM -0600, Bill Schmidt wrote:
> Adding the patch author for his information.

Thanks Bill.

> On 1/24/22 2:26 PM, Jakub Jelinek via Gcc-patches wrote:
> > expand_builtin_feclear_feraise_except doesn't check if op0 matches
> > the predicate of operands[1], the backend requires const_int_operand,
> > but because the call isn't done with a constant integer:
> > feraiseexcept (t == LLONG_MIN ? FE_INEXACT : FE_INVALID);
> > op0 is a REG.
> > If CONST_INT is what is expected on all targets, then it should punt if
> > op0 isn't one, otherwise it should the predicate.

My expectation was for backend expanders to be free to choose when they
expand, to be able to fail and cleanly fallback to libc call, and not
enforce one specific constrains to all targets.

I will look into it ASAP and see what can be done.
Thanks for the feedback.


o/
Raoni
  
Jakub Jelinek Jan. 24, 2022, 10:35 p.m. UTC | #5
On Mon, Jan 24, 2022 at 06:24:11PM -0300, Raoni Fassina Firmino wrote:
> On Mon, Jan 24, 2022 at 02:29:39PM -0600, Bill Schmidt wrote:
> > Adding the patch author for his information.
> 
> Thanks Bill.
> 
> > On 1/24/22 2:26 PM, Jakub Jelinek via Gcc-patches wrote:
> > > expand_builtin_feclear_feraise_except doesn't check if op0 matches
> > > the predicate of operands[1], the backend requires const_int_operand,
> > > but because the call isn't done with a constant integer:
> > > feraiseexcept (t == LLONG_MIN ? FE_INEXACT : FE_INVALID);
> > > op0 is a REG.
> > > If CONST_INT is what is expected on all targets, then it should punt if
> > > op0 isn't one, otherwise it should the predicate.
> 
> My expectation was for backend expanders to be free to choose when they
> expand, to be able to fail and cleanly fallback to libc call, and not
> enforce one specific constrains to all targets.
> 
> I will look into it ASAP and see what can be done.
> Thanks for the feedback.

These days the usual way of doing this is through
maybe_expand_insn and create_{output,input}_operand before that.

	Jakub
  
Raoni Fassina Firmino Jan. 25, 2022, 4:28 p.m. UTC | #6
On Mon, Jan 24, 2022 at 11:35:47PM +0100, AL gcc-patches wrote:
> On Mon, Jan 24, 2022 at 06:24:11PM -0300, Raoni Fassina Firmino wrote:
> > On Mon, Jan 24, 2022 at 02:29:39PM -0600, Bill Schmidt wrote:
> > > Adding the patch author for his information.
> > 
> > Thanks Bill.
> > 
> > > On 1/24/22 2:26 PM, Jakub Jelinek via Gcc-patches wrote:
> > > > expand_builtin_feclear_feraise_except doesn't check if op0 matches
> > > > the predicate of operands[1], the backend requires const_int_operand,

Below is a patch to do just that. In preliminary tests it seems to work.
What do you think aboud it Jakub?

> > > > but because the call isn't done with a constant integer:
> > > > feraiseexcept (t == LLONG_MIN ? FE_INEXACT : FE_INVALID);
> > > > op0 is a REG.
> > > > If CONST_INT is what is expected on all targets, then it should punt if
> > > > op0 isn't one, otherwise it should the predicate.
> > 
> > My expectation was for backend expanders to be free to choose when they
> > expand, to be able to fail and cleanly fallback to libc call, and not
> > enforce one specific constrains to all targets.
> > 
> > I will look into it ASAP and see what can be done.
> > Thanks for the feedback.
> 
> These days the usual way of doing this is through
> maybe_expand_insn and create_{output,input}_operand before that.

I looked into maybe_expand_insn, if I undestood correctly in my reading
of maybe_expand_insn I could remove mostly if not all code in
expand_builtin_feclear_feraise_except with it, even the validate_arglist
part in the beginning?

o/
Raoni

---
 gcc/builtins.cc | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/gcc/builtins.cc b/gcc/builtins.cc
index e84208035dab..88e689993563 100644
--- a/gcc/builtins.cc
+++ b/gcc/builtins.cc
@@ -2598,6 +2598,9 @@ expand_builtin_feclear_feraise_except (tree exp, rtx target,
   if (icode == CODE_FOR_nothing)
     return NULL_RTX;
 
+  if (!(*insn_data[icode].operand[1].predicate) (op0, GET_MODE(op0)))
+    return NULL_RTX;
+
   if (target == 0
       || GET_MODE (target) != target_mode
       || !(*insn_data[icode].operand[0].predicate) (target, target_mode))
  
Jakub Jelinek Jan. 25, 2022, 6:52 p.m. UTC | #7
On Tue, Jan 25, 2022 at 01:28:29PM -0300, Raoni Fassina Firmino wrote:
> Below is a patch to do just that. In preliminary tests it seems to work.
> What do you think aboud it Jakub?

Ok for trunk.

> > These days the usual way of doing this is through
> > maybe_expand_insn and create_{output,input}_operand before that.
> 
> I looked into maybe_expand_insn, if I undestood correctly in my reading
> of maybe_expand_insn I could remove mostly if not all code in
> expand_builtin_feclear_feraise_except with it, even the validate_arglist
> part in the beginning?

validate_arglist should be still performed.  But maybe_expand_insn +
create_*_operand will take care of checking the predicates etc.

> --- a/gcc/builtins.cc
> +++ b/gcc/builtins.cc
> @@ -2598,6 +2598,9 @@ expand_builtin_feclear_feraise_except (tree exp, rtx target,
>    if (icode == CODE_FOR_nothing)
>      return NULL_RTX;
>  
> +  if (!(*insn_data[icode].operand[1].predicate) (op0, GET_MODE(op0)))
> +    return NULL_RTX;
> +
>    if (target == 0
>        || GET_MODE (target) != target_mode
>        || !(*insn_data[icode].operand[0].predicate) (target, target_mode))
> -- 
> 2.34.1

	Jakub
  

Patch

diff --git a/gcc/builtins.c b/gcc/builtins.c
index c780340ed327..f52bff85f923 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);
@@ -2555,6 +2558,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.  */
@@ -7056,6 +7112,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 6f74075f58d8..5f1a7f047cec 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -6917,6 +6917,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 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.
+(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 target libc, and since they are free to
+;; choose the values and the expand needs to know them beforehand,
+;; this expander only expands for target libcs that it can handle the
+;; values it 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 target libc, and since they are free to
+;; choose the values and the expand needs to know them beforehand,
+;; this expander only expands for target libcs that it can handle the
+;; values it 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 637124a71722..0b2dc5f9d121 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -13485,6 +13485,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 19e89ae502bc..5568f9453233 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -6069,6 +6069,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 5fcf5386a0b3..f4afe5c75b5b 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..4482e89b0db9
--- /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..28c2a00ec520
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-2.c
@@ -0,0 +1,91 @@ 
+/* { 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.
+   The excepts parameter needs to be passed as constant to
+   __builtin_feclearexcept and __builtin_feraiseexcept because some bultins only
+   expand on constant input. */
+
+#include <fenv.h>
+
+#ifdef DEBUG
+#include <stdio.h>
+#define INFO(...) printf(__VA_ARGS__)
+#define FAIL(ret, raised, expected, excepts, excepts_str, func) \
+        printf("ERROR [l %d] testing %s (%x): %s returned %d."  \
+	       " Raised except bits %x, expecected %x\n",       \
+	       __LINE__, excepts_str, excepts, func, ret, raised, expected)
+#else
+void abort (void);
+#define INFO(...)
+#define FAIL(ret, raised, expected, excepts, excepts_str, func) abort()
+#endif
+
+#define TEST(excepts)                                                          \
+    do {                                                                       \
+      int ret = 0;                                                             \
+      int raised = 0;                                                          \
+                                                                               \
+      INFO("test: %s (%x)\n", #excepts, excepts);                              \
+                                                                               \
+      feclearexcept(FE_ALL_EXCEPT);                                            \
+      ret = __builtin_feraiseexcept(excepts);                                  \
+      raised = fetestexcept(FE_ALL_EXCEPT);                                    \
+      if (ret != 0 || raised != (excepts))                                     \
+        FAIL(ret, raised, excepts, excepts, #excepts,                          \
+	     "__builtin_feraiseexcept");                                       \
+                                                                               \
+      feraiseexcept(FE_ALL_EXCEPT);                                            \
+      ret = __builtin_feclearexcept(excepts);                                  \
+      raised = fetestexcept(FE_ALL_EXCEPT);                                    \
+      if (ret != 0 || raised != (FE_ALL_EXCEPT & ~(excepts)))                  \
+        FAIL(ret, raised, FE_ALL_EXCEPT & ~(excepts), excepts, #excepts,       \
+	     "__builtin_feclearexcept");                                       \
+    } while (0)
+
+int
+main ()
+{
+    TEST(0);
+    TEST(FE_ALL_EXCEPT);
+
+    TEST(FE_INVALID);
+    TEST(FE_DIVBYZERO);
+    TEST(FE_INEXACT);
+    TEST(FE_OVERFLOW);
+    TEST(FE_UNDERFLOW);
+
+    TEST(FE_INVALID | FE_DIVBYZERO);
+    TEST(FE_INVALID | FE_INEXACT);
+    TEST(FE_INVALID | FE_OVERFLOW);
+    TEST(FE_INVALID | FE_UNDERFLOW);
+    TEST(FE_DIVBYZERO | FE_INEXACT);
+    TEST(FE_DIVBYZERO | FE_OVERFLOW);
+    TEST(FE_DIVBYZERO | FE_UNDERFLOW);
+    TEST(FE_INEXACT | FE_OVERFLOW);
+    TEST(FE_INEXACT | FE_UNDERFLOW);
+    TEST(FE_OVERFLOW | FE_UNDERFLOW);
+
+    TEST(FE_INVALID | FE_DIVBYZERO | FE_INEXACT);
+    TEST(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
+    TEST(FE_INVALID | FE_DIVBYZERO | FE_UNDERFLOW);
+    TEST(FE_INVALID | FE_INEXACT | FE_OVERFLOW);
+    TEST(FE_INVALID | FE_INEXACT | FE_UNDERFLOW);
+    TEST(FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW);
+    TEST(FE_DIVBYZERO | FE_INEXACT | FE_OVERFLOW);
+    TEST(FE_DIVBYZERO | FE_INEXACT | FE_UNDERFLOW);
+    TEST(FE_DIVBYZERO | FE_OVERFLOW | FE_UNDERFLOW);
+    TEST(FE_INEXACT | FE_OVERFLOW | FE_UNDERFLOW);
+
+    TEST(FE_INVALID | FE_DIVBYZERO | FE_INEXACT | FE_UNDERFLOW);
+    TEST(FE_INVALID | FE_DIVBYZERO | FE_INEXACT | FE_OVERFLOW);
+    TEST(FE_INVALID | FE_DIVBYZERO | FE_UNDERFLOW | FE_OVERFLOW);
+    TEST(FE_INVALID | FE_INEXACT | FE_UNDERFLOW | FE_OVERFLOW);
+    TEST(FE_DIVBYZERO | FE_INEXACT | FE_UNDERFLOW | FE_OVERFLOW);
+
+  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..56ffc50e3ee3
--- /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 / sizeof rm[0]; i++)
+    {
+      fesetround(rm[i]);
+      rounding = __builtin_fegetround();
+      expected = fegetround();
+      if (rounding != expected)
+        FAIL(rounding, expected);
+    }
+
+  return 0;
+}