diff options
author | Wilco Dijkstra <wdijkstr@arm.com> | 2014-06-23 17:15:41 +0100 |
---|---|---|
committer | Will Newton <will.newton@linaro.org> | 2014-06-23 17:29:00 +0100 |
commit | e6d90d675d4cae810be76a5ff41b8ae8bd6bc914 (patch) | |
tree | 45d3f98b04e9395347b331c73c64846e823f3c95 /sysdeps | |
parent | 88a4647493fbc2a3f6b1348df010bb0a9c5aed55 (diff) | |
download | glibc-e6d90d675d4cae810be76a5ff41b8ae8bd6bc914.tar glibc-e6d90d675d4cae810be76a5ff41b8ae8bd6bc914.tar.gz glibc-e6d90d675d4cae810be76a5ff41b8ae8bd6bc914.tar.bz2 glibc-e6d90d675d4cae810be76a5ff41b8ae8bd6bc914.zip |
Add generic HAVE_RM_CTX implementation
This patch adds a generic implementation of HAVE_RM_CTX using standard
fenv calls. As a result math functions using SET_RESTORE_ROUND* macros
do not suffer from a large slowdown on targets which do not implement
optimized libc_fe*_ctx inline functions. Most of the libc_fe* inline
functions are now unused and could be removed in the future (there are
a few math functions left which use a mixture of standard fenv calls
and libc_fe* inline functions - they could be updated to use
SET_RESTORE_ROUND or improved to avoid expensive fenv manipulations
across just a few FP instructions).
libc_feholdsetround*_noex_ctx is added to enable better optimization of
SET_RESTORE_ROUND_NOEX* implementations.
Performance measurements on ARM and x86 of sin() show significant gains
over the current default, fairly close to a highly optimized fenv_private:
ARM x86
no fenv_private : 100% 100%
generic HAVE_RM_CTX : 250% 350%
fenv_private (CTX) : 250% 450%
2014-06-23 Will Newton <will.newton@linaro.org>
Wilco <wdijkstr@arm.com>
* sysdeps/generic/math_private.h: Add generic HAVE_RM_CTX
implementation. Include get-rounding-mode.h.
[!HAVE_RM_CTX]: Define HAVE_RM_CTX to zero.
[!libc_feholdsetround_noex_ctx]: Define
libc_feholdsetround_noex_ctx.
[!libc_feholdsetround_noexf_ctx]: Define
libc_feholdsetround_noexf_ctx.
[!libc_feholdsetround_noexl_ctx]: Define
libc_feholdsetround_noexl_ctx.
(libc_feholdsetround_ctx): New function.
(libc_feresetround_ctx): New function.
(libc_feholdsetround_noex_ctx): New function.
(libc_feresetround_noex_ctx): New function.
Diffstat (limited to 'sysdeps')
-rw-r--r-- | sysdeps/generic/math_private.h | 115 |
1 files changed, 96 insertions, 19 deletions
diff --git a/sysdeps/generic/math_private.h b/sysdeps/generic/math_private.h index 9b881a3e76..94c1e4a192 100644 --- a/sysdeps/generic/math_private.h +++ b/sysdeps/generic/math_private.h @@ -20,6 +20,7 @@ #include <stdint.h> #include <sys/types.h> #include <fenv.h> +#include <get-rounding-mode.h> /* The original fdlibm code used statements like: n0 = ((*(int*)&one)>>29)^1; * index of high word * @@ -551,12 +552,26 @@ default_libc_feupdateenv_test (fenv_t *e, int ex) # define libc_feresetround_noexl libc_fesetenvl #endif +#ifndef HAVE_RM_CTX +# define HAVE_RM_CTX 0 +#endif + #if HAVE_RM_CTX /* Set/Restore Rounding Modes only when necessary. If defined, these functions set/restore floating point state only if the state needed within the lexical block is different from the current state. This saves a lot of time when the floating point unit is much slower than the fixed point units. */ +# ifndef libc_feholdsetround_noex_ctx +# define libc_feholdsetround_noex_ctx libc_feholdsetround_ctx +# endif +# ifndef libc_feholdsetround_noexf_ctx +# define libc_feholdsetround_noexf_ctx libc_feholdsetroundf_ctx +# endif +# ifndef libc_feholdsetround_noexl_ctx +# define libc_feholdsetround_noexl_ctx libc_feholdsetroundl_ctx +# endif + # ifndef libc_feresetround_noex_ctx # define libc_feresetround_noex_ctx libc_fesetenv_ctx # endif @@ -567,24 +582,80 @@ default_libc_feupdateenv_test (fenv_t *e, int ex) # define libc_feresetround_noexl_ctx libc_fesetenvl_ctx # endif -# ifndef libc_feholdsetround_53bit_ctx -# define libc_feholdsetround_53bit_ctx libc_feholdsetround_ctx -# endif +#else -# ifndef libc_feresetround_53bit_ctx -# define libc_feresetround_53bit_ctx libc_feresetround_ctx -# endif +/* Default implementation using standard fenv functions. + Avoid unnecessary rounding mode changes by first checking the + current rounding mode. Note the use of __glibc_unlikely is + important for performance. */ -# define SET_RESTORE_ROUND_GENERIC(RM,ROUNDFUNC,CLEANUPFUNC) \ - struct rm_ctx ctx __attribute__((cleanup(CLEANUPFUNC ## _ctx))); \ - ROUNDFUNC ## _ctx (&ctx, (RM)) -#else -# define SET_RESTORE_ROUND_GENERIC(RM, ROUNDFUNC, CLEANUPFUNC) \ - fenv_t __libc_save_rm __attribute__((cleanup(CLEANUPFUNC))); \ - ROUNDFUNC (&__libc_save_rm, (RM)) +static __always_inline void +libc_feholdsetround_ctx (struct rm_ctx *ctx, int round) +{ + ctx->updated_status = false; + + /* Update rounding mode only if different. */ + if (__glibc_unlikely (round != get_rounding_mode ())) + { + ctx->updated_status = true; + fegetenv (&ctx->env); + fesetround (round); + } +} + +static __always_inline void +libc_feresetround_ctx (struct rm_ctx *ctx) +{ + /* Restore the rounding mode if updated. */ + if (__glibc_unlikely (ctx->updated_status)) + feupdateenv (&ctx->env); +} + +static __always_inline void +libc_feholdsetround_noex_ctx (struct rm_ctx *ctx, int round) +{ + /* Save exception flags and rounding mode. */ + fegetenv (&ctx->env); + + /* Update rounding mode only if different. */ + if (__glibc_unlikely (round != get_rounding_mode ())) + fesetround (round); +} + +static __always_inline void +libc_feresetround_noex_ctx (struct rm_ctx *ctx) +{ + /* Restore exception flags and rounding mode. */ + fesetenv (&ctx->env); +} + +# define libc_feholdsetroundf_ctx libc_feholdsetround_ctx +# define libc_feholdsetroundl_ctx libc_feholdsetround_ctx +# define libc_feresetroundf_ctx libc_feresetround_ctx +# define libc_feresetroundl_ctx libc_feresetround_ctx + +# define libc_feholdsetround_noexf_ctx libc_feholdsetround_noex_ctx +# define libc_feholdsetround_noexl_ctx libc_feholdsetround_noex_ctx +# define libc_feresetround_noexf_ctx libc_feresetround_noex_ctx +# define libc_feresetround_noexl_ctx libc_feresetround_noex_ctx + +#endif + +#ifndef libc_feholdsetround_53bit_ctx +# define libc_feholdsetround_53bit_ctx libc_feholdsetround_ctx +#endif +#ifndef libc_feresetround_53bit_ctx +# define libc_feresetround_53bit_ctx libc_feresetround_ctx #endif -/* Save and restore the rounding mode within a lexical block. */ +#define SET_RESTORE_ROUND_GENERIC(RM,ROUNDFUNC,CLEANUPFUNC) \ + struct rm_ctx ctx __attribute__((cleanup (CLEANUPFUNC ## _ctx))); \ + ROUNDFUNC ## _ctx (&ctx, (RM)) + +/* Set the rounding mode within a lexical block. Restore the rounding mode to + the value at the start of the block. The exception mode must be preserved. + Exceptions raised within the block must be set in the exception flags. + Non-stop mode may be enabled inside the block. */ #define SET_RESTORE_ROUND(RM) \ SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround, libc_feresetround) @@ -593,15 +664,21 @@ default_libc_feupdateenv_test (fenv_t *e, int ex) #define SET_RESTORE_ROUNDL(RM) \ SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetroundl, libc_feresetroundl) -/* Save and restore the rounding mode within a lexical block, and also - the set of exceptions raised within the block may be discarded. */ +/* Set the rounding mode within a lexical block. Restore the rounding mode to + the value at the start of the block. The exception mode must be preserved. + Exceptions raised within the block must be discarded, and exception flags + are restored to the value at the start of the block. + Non-stop mode may be enabled inside the block. */ #define SET_RESTORE_ROUND_NOEX(RM) \ - SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround, libc_feresetround_noex) + SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround_noex, \ + libc_feresetround_noex) #define SET_RESTORE_ROUND_NOEXF(RM) \ - SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetroundf, libc_feresetround_noexf) + SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround_noexf, \ + libc_feresetround_noexf) #define SET_RESTORE_ROUND_NOEXL(RM) \ - SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetroundl, libc_feresetround_noexl) + SET_RESTORE_ROUND_GENERIC (RM, libc_feholdsetround_noexl, \ + libc_feresetround_noexl) /* Like SET_RESTORE_ROUND, but also set rounding precision to 53 bits. */ #define SET_RESTORE_ROUND_53BIT(RM) \ |