From f60e9933a652a3b6ff5d3176f85a41833c1bc338 Mon Sep 17 00:00:00 2001 From: Alastair Bridgewater Date: Sun, 23 Oct 2011 22:36:24 -0400 Subject: [PATCH] Fix FP traps on OSX/x86-64. * 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 | 1 + src/code/float-trap.lisp | 3 ++- src/runtime/x86-64-darwin-os.c | 18 ++++++++++++++++++ src/runtime/x86-64-darwin-os.h | 3 +++ tests/float.pure.lisp | 25 +++++++++++++++++++++++-- 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 5393949..b141253 100644 --- 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. diff --git a/src/code/float-trap.lisp b/src/code/float-trap.lisp index 4bd83cb..86f402e 100644 --- a/src/code/float-trap.lisp +++ b/src/code/float-trap.lisp @@ -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 () diff --git a/src/runtime/x86-64-darwin-os.c b/src/runtime/x86-64-darwin-os.c index 180db11..86d4687 100644 --- a/src/runtime/x86-64-darwin-os.c +++ b/src/runtime/x86-64-darwin-os.c @@ -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 diff --git a/src/runtime/x86-64-darwin-os.h b/src/runtime/x86-64-darwin-os.h index 22cde5e..53b2230 100644 --- a/src/runtime/x86-64-darwin-os.h +++ b/src/runtime/x86-64-darwin-os.h @@ -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 */ diff --git a/tests/float.pure.lisp b/tests/float.pure.lisp index 60b3a36..a7d8e34 100644 --- a/tests/float.pure.lisp +++ b/tests/float.pure.lisp @@ -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)) @@ -125,7 +125,28 @@ (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 -- 1.7.10.4