diff --git a/sysdeps/powerpc/fpu/fenv_libc.h b/sysdeps/powerpc/fpu/fenv_libc.h index e8d40ea..b10b6a1 100644 --- a/sysdeps/powerpc/fpu/fenv_libc.h +++ b/sysdeps/powerpc/fpu/fenv_libc.h @@ -49,6 +49,38 @@ extern const fenv_t *__fe_mask_env (void) attribute_hidden; __fr; \ }) +#define __fe_mffscrn(rn) \ + ({register fenv_union_t __fr; \ + if (__builtin_constant_p (rn)) \ + __asm__ __volatile__ ( \ + ".machine push; .machine \"power9\"; mffscrni %0,%1; .machine pop" \ + : "=f" (__fr.fenv) : "i" (rn)); \ + else \ + { \ + __fr.l = (rn); \ + __asm__ __volatile__ ( \ + ".machine push; .machine \"power9\"; mffscrn %0,%1; .machine pop" \ + : "=f" (__fr.fenv) : "f" (__fr.fenv)); \ + } \ + __fr.fenv; \ + }) + +/* Like fegetenv_status, but also sets the rounding mode. */ +#ifdef _ARCH_PWR9 +#define fegetenv_and_set_rn(rn) __fe_mffscrn (rn) +#else +/* 'mffscrn' will decode to 'mffs' on ARCH < 3_00, which is still necessary + but not sufficient, because it does not set the rounding mode. + Explicitly set the rounding mode when 'mffscrn' actually doesn't. */ +#define fegetenv_and_set_rn(rn) \ + ({register fenv_union_t __fr; \ + __fr.fenv = __fe_mffscrn (rn); \ + if (__glibc_unlikely (!(GLRO(dl_hwcap2) & PPC_FEATURE2_ARCH_3_00))) \ + __fesetround_inline (rn); \ + __fr.fenv; \ + }) +#endif + /* Equivalent to fesetenv, but takes a fenv_t instead of a pointer. */ #define fesetenv_register(env) \ do { \ diff --git a/sysdeps/powerpc/fpu/fenv_private.h b/sysdeps/powerpc/fpu/fenv_private.h index b0149aa..30df92c 100644 --- a/sysdeps/powerpc/fpu/fenv_private.h +++ b/sysdeps/powerpc/fpu/fenv_private.h @@ -133,16 +133,7 @@ static __always_inline void libc_feresetround_ppc (fenv_t *envp) { fenv_union_t new = { .fenv = *envp }; - - /* If the old env has no enabled exceptions and the new env has any enabled - exceptions, then unmask SIGFPE in the MSR FE0/FE1 bits. This will put the - hardware into "precise mode" and may cause the FPU to run slower on some - hardware. */ - if ((new.l & _FPU_ALL_TRAPS) != 0) - (void) __fe_nomask_env_priv (); - - /* Atomically enable and raise (if appropriate) exceptions set in `new'. */ - fesetenv_mode (new.fenv); + fegetenv_and_set_rn (new.l & FPSCR_RN_MASK); } static __always_inline int @@ -184,22 +175,10 @@ libc_feupdateenv_ppc (fenv_t *e) static __always_inline void libc_feholdsetround_ppc_ctx (struct rm_ctx *ctx, int r) { - fenv_union_t old, new; + fenv_union_t old; - old.fenv = fegetenv_status (); - - new.l = (old.l & ~(FPSCR_ENABLES_MASK|FPSCR_RN_MASK)) | r; - - ctx->env = old.fenv; - if (__glibc_unlikely (new.l != old.l)) - { - if ((old.l & _FPU_ALL_TRAPS) != 0) - (void) __fe_mask_env (); - fesetenv_mode (new.fenv); - ctx->updated_status = true; - } - else - ctx->updated_status = false; + ctx->env = old.fenv = fegetenv_and_set_rn (r); + ctx->updated_status = (r != (old.l & FPSCR_RN_MASK)); } static __always_inline void