Fix FP traps on OSX/x86-64.
authorAlastair Bridgewater <nyef@virtdev-1.lisphacker.com>
Mon, 24 Oct 2011 02:36:24 +0000 (22:36 -0400)
committerAlastair Bridgewater <nyef@virtdev-1.lisphacker.com>
Mon, 24 Oct 2011 13:57:44 +0000 (09:57 -0400)
  * De-cripple SB-INT:SET-FLOATING-POINT-MODES for this platform.

  * Enable restoring the FPU control word during interrupt handling
on this platform (RESTORE_FP_CONTROL_FROM_CONTEXT).

  * Implement restoring the FPU control word on this platform
(os_restore_fp_control).

  * Update :FAILS-ON information for the now-passing tests.

  * Insert an addtional copy of test float.pure.lisp /
(ADDITION-OVERFLOW BUG-372) to detect failure to restore the FPU
control word in signal handling, with appropriate commentary.

NEWS
src/code/float-trap.lisp
src/runtime/x86-64-darwin-os.c
src/runtime/x86-64-darwin-os.h
tests/float.pure.lisp

diff --git a/NEWS b/NEWS
index 5393949..b141253 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,7 @@ changes relative to sbcl-1.0.52:
     GETF, LOGBITP, LDB, and MASK-FIELD now arrange for non-primary values
     of multiple-valued places to be set to NIL, instead of signalling an
     error (per a careful reading of CLHS 5.1.2.3).
+  * bug fix: floating-point traps now work on darwin/x86-64.
 
 changes in sbcl-1.0.52 relative to sbcl-1.0.51:
   * enhancement: ASDF has been updated to version 2.017.
index 4bd83cb..86f402e 100644 (file)
@@ -118,7 +118,8 @@ in effect."
             (or (cdr (assoc precision *precision-mode-alist*))
                 (error "unknown precision mode: ~S" precision))))
     ;; FIXME: This apparently doesn't work on Darwin
-    #!-darwin (setf (floating-point-modes) modes))
+    #!-(and darwin (or ppc x86))
+    (setf (floating-point-modes) modes))
   (values))
 
 (defun get-floating-point-modes ()
index 180db11..86d4687 100644 (file)
@@ -49,6 +49,9 @@ typedef struct __darwin_mcontext64 darwin_mcontext;
 #define es __es
 #define fs __fs
 
+#define fpu_fcw __fpu_fcw
+#define fpu_mxcsr __fpu_mxcsr
+
 #else
 
 typedef struct ucontext darwin_ucontext;
@@ -587,4 +590,19 @@ catch_exception_raise(mach_port_t exception_port,
     return ret;
 }
 
+void
+os_restore_fp_control(os_context_t *context)
+{
+    /* KLUDGE: The x87 FPU control word is some nasty bitfield struct
+     * thing.  Rather than deal with that, just grab it as a 16-bit
+     * integer. */
+    unsigned short fpu_control_word =
+        *((unsigned short *)&context->uc_mcontext->fs.fpu_fcw);
+    /* reset exception flags and restore control flags on SSE2 FPU */
+    unsigned int temp = (context->uc_mcontext->fs.fpu_mxcsr) & ~0x3F;
+    asm ("ldmxcsr %0" : : "m" (temp));
+    /* same for x87 FPU. */
+    asm ("fldcw %0" : : "m" (fpu_control_word));
+}
+
 #endif
index 22cde5e..53b2230 100644 (file)
@@ -16,4 +16,7 @@ static inline os_context_t *arch_os_get_context(void **void_context)
 #define CONTEXT_ADDR_FROM_STEM(stem) &context->uc_mcontext->ss.stem
 #endif /* __DARWIN_UNIX03 */
 
+#define RESTORE_FP_CONTROL_FROM_CONTEXT
+void os_restore_fp_control(os_context_t *context);
+
 #endif /* _X86_64_DARWIN_OS_H */
index 60b3a36..a7d8e34 100644 (file)
@@ -93,7 +93,7 @@
 (assert (= 0.0d0 (scale-float 1.0d0 (1- most-negative-fixnum))))
 
 (with-test (:name (:scale-float-overflow :bug-372)
-            :fails-on :darwin) ;; bug 372
+            :fails-on '(and :darwin (or :ppc :x86))) ;; bug 372
   (progn
     (assert (raises-error? (scale-float 1.0 most-positive-fixnum)
                            floating-point-overflow))
 (funcall (compile nil '(lambda () (tan (tan (round 0))))))
 
 (with-test (:name (:addition-overflow :bug-372)
-            :fails-on '(or (and :ppc :openbsd) :darwin (and :x86 :netbsd)))
+            :fails-on '(or (and :ppc :openbsd)
+                           (and (or :ppc :x86) :darwin)
+                           (and :x86 :netbsd)))
+  (assert (typep (nth-value
+                  1
+                  (ignore-errors
+                    (sb-sys:without-interrupts
+                     (sb-int:set-floating-point-modes :current-exceptions nil
+                                                      :accrued-exceptions nil)
+                     (loop repeat 2 summing most-positive-double-float)
+                     (sleep 2))))
+                 'floating-point-overflow)))
+
+;; This is the same test as above.  Even if the above copy passes,
+;; this copy will fail if SIGFPE handling ends up clearing the FPU
+;; control word, which can happen if the kernel clears the FPU control
+;; (a reasonable thing for it to do) and the runtime fails to
+;; compensate for this (see RESTORE_FP_CONTROL_WORD in interrupt.c).
+(with-test (:name (:addition-overflow :bug-372 :take-2)
+            :fails-on '(or (and :ppc :openbsd)
+                           (and (or :ppc :x86) :darwin)
+                           (and :x86 :netbsd)))
   (assert (typep (nth-value
                   1
                   (ignore-errors