On Fri, 2020-09-04 at 12:52 -0300, Raoni Fassina Firmino via Gcc-patches wrote: > Changes since v1[1]: > - Fixed english spelling; > - Fixed code-style; > - Changed match operand predicate in feclearexcept and feraiseexcept; > - Changed testcase options; > - Minor changes in test code to be C90 compatible; > - Other minor changes sugested by Segher; > - Changed subject line tag (not sure if I tagged correctly or should > include optabs: also) > > There is one pending question raised by Segher, It is about adding > documentation, I am not sure if it is needed and if so, where it > should be. I will quote the relevant part of the conversation[2] from > the v1 thread for context: > > > > > +OPTAB_D (fegetround_optab, "fegetround$a") > > > > +OPTAB_D (feclearexcept_optab, "feclearexcept$a") > > > > +OPTAB_D (feraiseexcept_optab, "feraiseexcept$a") > > >␣ > > > Should those be documented somewhere? (In gcc/doc/ somewhere). > > > > I am lost on that one. I took a look on the docs (I hope looking on the > > online docs was good enough) and I didn't find a place where i feel it > > sits well. On the PowerPC target specific sections (6.60.22 Basic > > PowerPC Built-in Functions), I didn't found it mentioning builtins that > > are optimizations for the standard library functions, but we do have > > many of these for Power. Then, on the generic section (6.59 Other > > Built-in Functions Provided by GCC) it mentions C99 functions that have > > builtins but it seems like it mentions builtins that have target > > independent implementation, or at least it dos not say that some > > builtins may be implemented on only some targets. And in this case > > there is no implementation (for now) for any other target that is not > > PowerPc. > > > > So, I don't know if or where this should be documented. > > tested on top of master (c5a6c2237a1156dc43fa32c938fc32acdfc2bff9) > on the following plataforms with no regression: > - powerpc64le-linux-gnu (Power 9) > - powerpc64le-linux-gnu (Power 8) > > [1] https://gcc.gnu.org/pipermail/gcc-patches/2020-August/552024.html > [2] https://gcc.gnu.org/pipermail/gcc-patches/2020-September/553295.html > > ---- 8< ---- > > This optimizations were originally in glibc, but was removed > and sugested that they were a good fit as gcc builtins[1]. > > 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 <ra...@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. > * 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 few cosmetic nits below,.. this is perhaps enough activity to get others to take a look. :-) > Signed-off-by: Raoni Fassina Firmino <ra...@linux.ibm.com> > > FIX: patch_v1 (2) > --- > gcc/builtins.c | 76 ++++++++++ > gcc/config/rs6000/rs6000.md | 82 +++++++++++ > gcc/optabs.def | 4 + > .../builtin-feclearexcept-feraiseexcept-1.c | 68 +++++++++ > .../builtin-feclearexcept-feraiseexcept-2.c | 133 ++++++++++++++++++ > .../gcc.target/powerpc/builtin-fegetround.c | 36 +++++ > 6 files changed, 399 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 > > diff --git a/gcc/builtins.c b/gcc/builtins.c > index 97f1a184dc6..a6f6141edb7 100644 > --- a/gcc/builtins.c > +++ b/gcc/builtins.c > @@ -115,6 +115,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); > @@ -2701,6 +2704,59 @@ expand_builtin_sincos (tree exp) > return const0_rtx; > } > > +/* Expand call EXP to the fegetround builtin (from C99 venv.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; > +} I don't see any references to feclearexcept or feraiseexcept in the functions there. I see the callers pass in those values via optab, but nothing in these functions explicitly checks or limits that in a way that is obvious upon my reading... Thus I wonder if there may be a different generic names that would be appropriate for the functions. > + > /* 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. */ > @@ -8290,6 +8346,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 43b620ae1c0..c19908040d1 100644 > --- a/gcc/config/rs6000/rs6000.md > +++ b/gcc/config/rs6000/rs6000.md > @@ -6565,6 +6565,88 @@ > [(set_attr "type" "fpload") > (set_attr "length" "8") > (set_attr "isa" "*,p8v,p8v")]) > + > +;; int __builtin_fegetround() > +(define_expand "fegetroundsi" > + [(use (match_operand:SI 0 "gpc_reg_operand"))] > + "TARGET_HARD_FLOAT" > +{ > + 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 = simplify_gen_subreg (SImode, tmp_di_2, DImode, 0); > + 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 know at compile time and specifying only one of known specifies > +;; 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 unsuported because it maps to more than > +;; one bit on FPSCR register. > +;; Because of these restrictions, this only expands on the desired cases. > +(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" > +{ > + switch (INTVAL (operands[1])) > + { > + case 0x2000000: /* FE_INEXACT */ > + case 0x4000000: /* FE_DIVBYZERO */ > + case 0x8000000: /* FE_UNDERFLOW */ > + case 0x10000000: /* FE_OVERFLOW */ > + break; > + default: > + FAIL; > + } > + > + rtx tmp = gen_rtx_CONST_INT (SImode, __builtin_clz (INTVAL (operands[1]))); > + emit_insn (gen_rs6000_mtfsb0 (tmp)); > + emit_move_insn (operands[0], const0_rtx); > + DONE; > +}) > + > +;; int fegraiseexcept(int excepts) > +;; > +;; This expansion for the C99 function only works when excepts is a > +;; constant know at compile time and specifying only one of known specifies > +;; 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 on FPSCR register. Should FE_INVALID have an explicit case statement path to FAIL? > +;; Because of these restrictions, this only expands on the desired cases. > +(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" > +{ > + switch (INTVAL (operands[1])) > + { > + case 0x2000000: /* FE_INEXACT */ > + case 0x4000000: /* FE_DIVBYZERO */ > + case 0x8000000: /* FE_UNDERFLOW */ > + case 0x10000000: /* FE_OVERFLOW */ > + break; > + default: > + FAIL; > + } > + > + rtx tmp = gen_rtx_CONST_INT (SImode, __builtin_clz (INTVAL (operands[1]))); > + emit_insn (gen_rs6000_mtfsb1 (tmp)); > + emit_move_insn (operands[0], const0_rtx); > + DONE; > +}) > + thwack extra line ? > > ;; 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/optabs.def b/gcc/optabs.def > index 78409aa1453..987ee0f79dc 100644 > --- a/gcc/optabs.def > +++ b/gcc/optabs.def > @@ -318,6 +318,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 00000000000..a2977d188e5 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-1.c > @@ -0,0 +1,68 @@ > +/* { 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 gracefull to a function call, and don't ICE during > + * compilation. */ gracefully > + > +#include <fenv.h> > + > +/* We use __builtin_* version to avoid the function be replaced by glibc that > + * may have an inline optimization for it in fenv.h. */ s/to avoid .../to force the use of the gcc implementation of the builtin, and avoid the glibc implementation./ ? No further comments or nits.. thanks -WIll > +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(0); // CALL > + __builtin_feclearexcept(FE_ALL_EXCEPT); // CALL > + __builtin_feclearexcept(FE_INVALID); // CALL > + __builtin_feclearexcept(FE_INEXACT | FE_DIVBYZERO); // CALL > + __builtin_feclearexcept(FE_INEXACT); // EXPAND > + __builtin_feclearexcept(FE_DIVBYZERO); // EXPAND > + __builtin_feclearexcept(FE_UNDERFLOW); // EXPAND > + __builtin_feclearexcept(FE_OVERFLOW); // 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(0); // CALL > + __builtin_feraiseexcept(FE_ALL_EXCEPT); // CALL > + __builtin_feraiseexcept(FE_INVALID); // CALL > + __builtin_feraiseexcept(FE_INEXACT | FE_DIVBYZERO); // CALL > + __builtin_feraiseexcept(FE_INEXACT); // EXPAND > + __builtin_feraiseexcept(FE_DIVBYZERO); // EXPAND > + __builtin_feraiseexcept(FE_UNDERFLOW); // EXPAND > + __builtin_feraiseexcept(FE_OVERFLOW); // 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 00000000000..4eb2a63d48f > --- /dev/null > +++ b/gcc/testsuite/gcc.target/powerpc/builtin-feclearexcept-feraiseexcept-2.c > @@ -0,0 +1,133 @@ > +/* { 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. */ > + > +#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 > + > +/* We use __builtin_* version to avoid the function be replaced by glibc that > + * may have an inline optimization for it in fenv.h. */ > +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 = "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"); > + > + 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 00000000000..c3649f874a5 > --- /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. */ > + > +#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 > + > +/* We use __builtin_* version to avoid the function be replaced by glibc that > + * may have an inline optimization for it in fenv.h. */ > +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; > +}