diff options
author | Raoni Fassina Firmino <raoni@linux.ibm.com> | 2022-01-13 14:08:53 -0300 |
---|---|---|
committer | Segher Boessenkool <segher@kernel.crashing.org> | 2022-01-24 17:00:11 +0000 |
commit | 4343f5e256791a5abaaef29fe1f831a03bab129e (patch) | |
tree | 5847305e8cfd8ad62d664399b54252788735ee65 | |
parent | b1aa2a3cf1ba233ccd5a49fde85a2ca7e5f620e9 (diff) | |
download | gcc-4343f5e256791a5abaaef29fe1f831a03bab129e.zip gcc-4343f5e256791a5abaaef29fe1f831a03bab129e.tar.gz gcc-4343f5e256791a5abaaef29fe1f831a03bab129e.tar.bz2 |
rtl: builtins: (not just) rs6000: Add builtins for fegetround, feclearexcept and feraiseexcept [PR94193]
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.
[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/
PR target/94193
* builtins.cc (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/
PR target/94193
* 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>
-rw-r--r-- | gcc/builtins.cc | 76 | ||||
-rw-r--r-- | gcc/config/rs6000/rs6000.md | 111 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 8 | ||||
-rw-r--r-- | gcc/doc/md.texi | 17 | ||||
-rw-r--r-- | gcc/optabs.def | 4 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-1.c | 76 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-2.c | 91 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/powerpc/builtin-fegetround.c | 36 |
8 files changed, 419 insertions, 0 deletions
diff --git a/gcc/builtins.cc b/gcc/builtins.cc index bf07341..e842080 100644 --- a/gcc/builtins.cc +++ b/gcc/builtins.cc @@ -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 4e22118..fdfbc65 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -6912,6 +6912,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 0775d97..a961fc4 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -13506,6 +13506,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 7f0aab9..feacb12 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -6087,6 +6087,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 bb46083..801310e 100644 --- a/gcc/optabs.def +++ b/gcc/optabs.def @@ -331,6 +331,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 0000000..4482e89 --- /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 0000000..28c2a00 --- /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 0000000..56ffc50 --- /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; +} |