about summary refs log tree commit diff
path: root/sysdeps/arm/feupdateenv.c
diff options
context:
space:
mode:
authorWilco <wdijkstr@arm.com>2014-06-24 13:53:04 +0000
committerWilco <wdijkstr@arm.com>2014-06-24 13:53:04 +0000
commitdb59bad394ef61bf6d6ef7916012f2a09d0b3d11 (patch)
tree9605e6411433339e020a53841702db0171efaf81 /sysdeps/arm/feupdateenv.c
parent001f7b773c637560ecfa686452a5e68d60d07db3 (diff)
downloadglibc-db59bad394ef61bf6d6ef7916012f2a09d0b3d11.tar.gz
glibc-db59bad394ef61bf6d6ef7916012f2a09d0b3d11.tar.xz
glibc-db59bad394ef61bf6d6ef7916012f2a09d0b3d11.zip
Rewrite feupdateenv
This patch rewrites feupdateenv to improve performance by avoiding
unnecessary FPSCR reads/writes. It fixes bug 16918 by passing the
correct return value.

2014-06-24  Wilco  <wdijkstr@arm.com>

	[BZ #16918]
	* sysdeps/arm/feupdateenv.c (feupdateenv):
	Rewrite to reduce FPSCR accesses and fix return value.
Diffstat (limited to 'sysdeps/arm/feupdateenv.c')
-rw-r--r--sysdeps/arm/feupdateenv.c44
1 files changed, 38 insertions, 6 deletions
diff --git a/sysdeps/arm/feupdateenv.c b/sysdeps/arm/feupdateenv.c
index 55a15025c6..d8116789d0 100644
--- a/sysdeps/arm/feupdateenv.c
+++ b/sysdeps/arm/feupdateenv.c
@@ -18,26 +18,58 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <fenv.h>
-#include <fpu_control.h>
 #include <arm-features.h>
 
 
 int
 feupdateenv (const fenv_t *envp)
 {
-  fpu_control_t fpscr;
+  fpu_control_t fpscr, new_fpscr, updated_fpscr;
+  int excepts;
 
   /* Fail if a VFP unit isn't present.  */
   if (!ARM_HAVE_VFP)
     return 1;
 
   _FPU_GETCW (fpscr);
+  excepts = fpscr & FE_ALL_EXCEPT;
 
-  /* Install new environment.  */
-  fesetenv (envp);
+  if ((envp != FE_DFL_ENV) && (envp != FE_NOMASK_ENV))
+    {
+      /* Merge current exception flags with the saved fenv.  */
+      new_fpscr = envp->__cw | excepts;
+
+      /* Write new FPSCR if different (ignoring NZCV flags).  */
+      if (((fpscr ^ new_fpscr) & ~_FPU_MASK_NZCV) != 0)
+	_FPU_SETCW (new_fpscr);
+
+      /* Raise the exceptions if enabled in the new FP state.  */
+      if (excepts & (new_fpscr >> FE_EXCEPT_SHIFT))
+	return feraiseexcept (excepts);
+
+      return 0;
+    }
+
+  /* Preserve the reserved FPSCR flags.  */
+  new_fpscr = fpscr & (_FPU_RESERVED | FE_ALL_EXCEPT);
+  new_fpscr |= (envp == FE_DFL_ENV) ? _FPU_DEFAULT : _FPU_IEEE;
+
+  if (((new_fpscr ^ fpscr) & ~_FPU_MASK_NZCV) != 0)
+    {
+      _FPU_SETCW (new_fpscr);
+
+      /* Not all VFP architectures support trapping exceptions, so
+	 test whether the relevant bits were set and fail if not.  */
+      _FPU_GETCW (updated_fpscr);
+
+      if (new_fpscr & ~updated_fpscr)
+	return 1;
+    }
+
+  /* Raise the exceptions if enabled in the new FP state.  */
+  if (excepts & (new_fpscr >> FE_EXCEPT_SHIFT))
+    return feraiseexcept (excepts);
 
-  /* Raise the saved exceptions.  */
-  feraiseexcept (fpscr & FE_ALL_EXCEPT);
   return 0;
 }
 libm_hidden_def (feupdateenv)