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
@@ -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 ();
@@ -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
@@ -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
@@ -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
@@ -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")
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}