Skip to content
Snippets Groups Projects
Commit 94bb2d9d authored by Raymond Toy's avatar Raymond Toy
Browse files

Merge branch 'rtoy-15-handle-fpu-exceptions' into 'master'

Handle FPU exceptions better

Fix #15 

Handle FPU exceptions better in sigfpe-handler.  Linux now passes all of the tests, including the inexact tests.

We can correctly print out the operands (if available) when we receive an inexact exception.

The floating point modes are preserved as much as possible when we throw from an arithmetic-error.

See merge request !7
parents 5c10ab93 1fab9bf3
No related branches found
No related tags found
No related merge requests found
...@@ -319,102 +319,118 @@ ...@@ -319,102 +319,118 @@
(alien:sap-alien scp (* unix:sigcontext)))) (alien:sap-alien scp (* unix:sigcontext))))
(traps (logand (ldb float-exceptions-byte modes) (traps (logand (ldb float-exceptions-byte modes)
(ldb float-traps-byte modes)))) (ldb float-traps-byte modes))))
#+(and darwin ppc)
(let* ((new-modes modes)
(new-exceptions (logandc2 (ldb float-exceptions-byte new-modes)
traps)))
;; (format t "sigfpe: modes = #B~32,'0b~%" modes)
;; (format t "sigfpe: new-exc = #B~32,'0b~%" new-exceptions)
(setf (ldb float-exceptions-byte new-modes) new-exceptions)
;; Clear out all exceptions and save them to the context.
;;
;; XXX: Should we just clear out the bits for the traps that are
;; enabled? If we did that then the accrued exceptions would be
;; correct.
(setf (ldb float-sticky-bits new-modes) 0)
;; Clear out the various sticky invalid operation bits too.
;;
;; XXX: Should we only do that if the invalid trap is enabled?
(setf (ldb float-invalid-op-1-byte new-modes) 0)
(setf (ldb float-invalid-op-2-byte new-modes) 0)
;; Clear the FP exception summary bit too.
(setf (ldb float-exceptions-summary-byte new-modes) 0)
;; (format t "sigfpe: new modes = #B~32,'0b~%" new-modes)
(setf (floating-point-modes) new-modes)
(setf (sigcontext-floating-point-modes
(alien:sap-alien scp (* unix:sigcontext)))
new-modes))
#+sse2
(let* ((new-modes modes)
(new-exceptions (logandc2 (ldb float-exceptions-byte new-modes)
traps)))
;; Clear out the status for any enabled traps. With SSE2, if
;; the current exception is enabled, the next FP instruction
;; will cause the exception to be signaled again. Hence, we
;; need to clear out the exceptions that we are handling here.
(setf (ldb float-exceptions-byte new-modes) new-exceptions)
;; XXX: This seems not right. Shouldn't we be setting the modes
;; in the sigcontext instead? This however seems to do what we
;; want.
(setf (vm:floating-point-modes) new-modes))
(multiple-value-bind (fop operands) (multiple-value-bind (fop operands)
(let ((sym (find-symbol "GET-FP-OPERANDS" "VM"))) (let ((sym (find-symbol "GET-FP-OPERANDS" "VM")))
(if (fboundp sym) (if (fboundp sym)
(funcall sym (alien:sap-alien scp (* unix:sigcontext)) modes) (funcall sym (alien:sap-alien scp (* unix:sigcontext)) modes)
(values nil nil))) (values nil nil)))
(cond ((not (zerop (logand float-divide-by-zero-trap-bit traps))) ;; Don't let throws get away without resetting the
(error 'division-by-zero ;; floating-point modes back to the original values which we get
:operation fop ;; from the sigcontext. Because we can throw, we never return
:operands operands)) ;; from the signal handler so the sigcontext is never restored.
((not (zerop (logand float-invalid-trap-bit traps))) ;; This means we need to restore the fpu state ourselves.
(error 'floating-point-invalid-operation (unwind-protect
:operation fop (cond ((not (zerop (logand float-divide-by-zero-trap-bit traps)))
:operands operands)) (error 'division-by-zero
((not (zerop (logand float-overflow-trap-bit traps))) :operation fop
(error 'floating-point-overflow :operands operands))
:operation fop ((not (zerop (logand float-invalid-trap-bit traps)))
:operands operands)) (error 'floating-point-invalid-operation
((not (zerop (logand float-underflow-trap-bit traps))) :operation fop
(error 'floating-point-underflow :operands operands))
:operation fop ((not (zerop (logand float-overflow-trap-bit traps)))
:operands operands)) (error 'floating-point-overflow
((not (zerop (logand float-inexact-trap-bit traps))) :operation fop
(error 'floating-point-inexact :operands operands))
:operation fop ((not (zerop (logand float-underflow-trap-bit traps)))
:operands operands)) (error 'floating-point-underflow
#+x86 :operation fop
((not (zerop (logand float-denormal-trap-bit traps))) :operands operands))
(error 'floating-point-denormal-operand ((not (zerop (logand float-inexact-trap-bit traps)))
:operation fop (error 'floating-point-inexact
:operands operands)) :operation fop
(t :operands operands))
;; It looks like the sigcontext on Solaris/x86 doesn't #+x86
;; actually save the status word of the FPU. The ((not (zerop (logand float-denormal-trap-bit traps)))
;; operands also seem to be missing. Signal a general (error 'floating-point-denormal-operand
;; arithmetic error. :operation fop
#+(and x86 solaris) :operands operands))
(error _"SIGFPE with no exceptions currently enabled? (si-code = ~D)" (t
code) ;; It looks like the sigcontext on Solaris/x86 doesn't
;; For all other x86 ports, we should only get here if ;; actually save the status word of the FPU. The
;; the SIGFPE was caused by an integer overflow on ;; operands also seem to be missing. Signal a general
;; division. For sparc and ppc, I (rtoy) don't think ;; arithmetic error.
;; there's any other way to get here since integer #+(and x86 solaris)
;; overflows aren't signaled. (error _"SIGFPE with no exceptions currently enabled? (si-code = ~D)"
;; code)
;; In that case, FOP should be /, so we can generate a ;; For all other x86 ports, we should only get here if
;; nice arithmetic-error. It's possible to use CODE, ;; the SIGFPE was caused by an integer overflow on
;; which is supposed to indicate what caused the ;; division. For sparc and ppc, I (rtoy) don't think
;; exception, but each OS is different, so we don't; FOP ;; there's any other way to get here since integer
;; can tell us. ;; overflows aren't signaled.
#-(and x86 solaris) ;;
(if fop ;; In that case, FOP should be /, so we can generate a
(error 'arithmetic-error ;; nice arithmetic-error. It's possible to use CODE,
:operation fop ;; which is supposed to indicate what caused the
:operands operands) ;; exception, but each OS is different, so we don't; FOP
(error _"SIGFPE with no exceptions currently enabled? (si-code = ~D)" ;; can tell us.
code))))))) #-(and x86 solaris)
(if fop
(error 'arithmetic-error
:operation fop
:operands operands)
(error _"SIGFPE with no exceptions currently enabled? (si-code = ~D)"
code))))
;; Cleanup
(let* ((new-modes modes)
(new-exceptions (logandc2 (ldb float-exceptions-byte new-modes)
traps)))
#+(and darwin ppc)
(progn
;; (format t "sigfpe: modes = #B~32,'0b~%" modes)
;; (format t "sigfpe: new-exc = #B~32,'0b~%" new-exceptions)
(setf (ldb float-exceptions-byte new-modes) new-exceptions)
;; Clear out all exceptions.
;;
;; XXX: Should we just clear out the bits for the traps that are
;; enabled? If we did that then the accrued exceptions would be
;; correct.
(setf (ldb float-sticky-bits new-modes) 0)
;; Clear out the various sticky invalid operation bits too.
;;
;; XXX: Should we only do that if the invalid trap is enabled?
(setf (ldb float-invalid-op-1-byte new-modes) 0)
(setf (ldb float-invalid-op-2-byte new-modes) 0)
;; Clear the FP exception summary bit too.
(setf (ldb float-exceptions-summary-byte new-modes) 0)
;; (format t "sigfpe: new modes = #B~32,'0b~%" new-modes)
(setf (floating-point-modes) new-modes))
#+sse2
(progn
;; Clear out the status for any enabled traps. With SSE2, if
;; the current exception is enabled, the next FP instruction
;; will cause the exception to be signaled again. Hence, we
;; need to clear out the exceptions that we are handling here.
(setf (ldb float-exceptions-byte new-modes) new-exceptions)
#+nil
(progn
(format *debug-io* "sigcontext modes: #x~4x (~A)~%"
modes (decode-floating-point-modes modes))
(format *debug-io* "current modes: #x~4x (~A)~%"
(vm:floating-point-modes) (get-floating-point-modes))
(format *debug-io* "new modes: #x~x (~A)~%"
new-modes (decode-floating-point-modes new-modes)))
(setf (vm:floating-point-modes) new-modes))
#-(or sse2 (and darwin ppc))
(progn
;; Apparently nothing needed for sparc it seems The FPU
;; state in the signal handler is unchanged and it seems we
;; don't need to reset it any way when we throw out.
))))))
(macrolet (macrolet
((with-float-traps (name merge-traps docstring) ((with-float-traps (name merge-traps docstring)
...@@ -440,6 +456,10 @@ ...@@ -440,6 +456,10 @@
;; representing the invalid operation. Otherwise, if we ;; representing the invalid operation. Otherwise, if we
;; enable the invalid trap later, these sticky bits will cause ;; enable the invalid trap later, these sticky bits will cause
;; an exception. ;; an exception.
;;
;; FIXME: Consider removing these for ppc. Since
;; we now restore the original modes exactly, I
;; don't think these are needed anymore.
#+ppc #+ppc
(invalid-mask (if (member :invalid traps) (invalid-mask (if (member :invalid traps)
(dpb 0 (dpb 0
...@@ -456,14 +476,8 @@ ...@@ -456,14 +476,8 @@
(logand (,',merge-traps ,orig-modes ,trap-mask) (logand (,',merge-traps ,orig-modes ,trap-mask)
,exception-mask))) ,exception-mask)))
,@body) ,@body)
;; Restore the original traps and exceptions. ;; Restore the modes exactly as they were.
(setf (floating-point-modes) (setf (floating-point-modes) ,orig-modes)))))))))
(logior (logand ,orig-modes ,(logior traps exceptions))
(logand (floating-point-modes)
,(logand trap-mask exception-mask)
#+ppc
,invalid-mask
#+mips ,(dpb 0 float-exceptions-byte #xffffffff))))))))))))
;; WITH-FLOAT-TRAPS-MASKED -- Public ;; WITH-FLOAT-TRAPS-MASKED -- Public
(with-float-traps masked logand (with-float-traps masked logand
......
...@@ -252,7 +252,9 @@ interrupt_handle_now(HANDLER_ARGS) ...@@ -252,7 +252,9 @@ interrupt_handle_now(HANDLER_ARGS)
handler = interrupt_handlers[signal]; handler = interrupt_handlers[signal];
#if 0
RESTORE_FPU(context); RESTORE_FPU(context);
#endif
if (handler.c == (void (*)(HANDLER_ARGS)) SIG_IGN) if (handler.c == (void (*)(HANDLER_ARGS)) SIG_IGN)
return; return;
...@@ -333,7 +335,9 @@ maybe_now_maybe_later(HANDLER_ARGS) ...@@ -333,7 +335,9 @@ maybe_now_maybe_later(HANDLER_ARGS)
setup_pending_signal(signal, code, context); setup_pending_signal(signal, code, context);
arch_set_pseudo_atomic_interrupted(context); arch_set_pseudo_atomic_interrupted(context);
} else { } else {
#if 0
RESTORE_FPU(context); RESTORE_FPU(context);
#endif
interrupt_handle_now(signal, code, context); interrupt_handle_now(signal, code, context);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment