aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/generic
diff options
context:
space:
mode:
authorWilco Dijkstra <wdijkstr@arm.com>2014-06-23 17:15:41 +0100
committerWill Newton <will.newton@linaro.org>2014-06-23 17:29:00 +0100
commite6d90d675d4cae810be76a5ff41b8ae8bd6bc914 (patch)
tree45d3f98b04e9395347b331c73c64846e823f3c95 /sysdeps/generic
parent88a4647493fbc2a3f6b1348df010bb0a9c5aed55 (diff)
downloadglibc-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/generic')
-rw-r--r--sysdeps/generic/math_private.h115
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) \