Tighter floating-point type constraints in some cases
[sbcl.git] / src / code / float-trap.lisp
1 ;;;; This file contains stuff for controlling floating point traps. It
2 ;;;; is IEEE float specific, but should work for pretty much any FPU
3 ;;;; where the state fits in one word and exceptions are represented
4 ;;;; by bits being set.
5
6 ;;;; This software is part of the SBCL system. See the README file for
7 ;;;; more information.
8 ;;;;
9 ;;;; This software is derived from the CMU CL system, which was
10 ;;;; written at Carnegie Mellon University and released into the
11 ;;;; public domain. The software is in the public domain and is
12 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
13 ;;;; files for more information.
14
15 (in-package "SB!VM")
16
17 (eval-when (:compile-toplevel :load-toplevel :execute)
18
19 (defparameter *float-trap-alist*
20   (list (cons :underflow float-underflow-trap-bit)
21         (cons :overflow float-overflow-trap-bit)
22         (cons :inexact float-inexact-trap-bit)
23         (cons :invalid float-invalid-trap-bit)
24         (cons :divide-by-zero float-divide-by-zero-trap-bit)
25         #!+x86 (cons :denormalized-operand float-denormal-trap-bit)))
26
27 (defparameter *rounding-mode-alist*
28   (list (cons :nearest float-round-to-nearest)
29         (cons :zero float-round-to-zero)
30         (cons :positive-infinity float-round-to-positive)
31         (cons :negative-infinity float-round-to-negative)))
32
33 #!+x86
34 (defparameter *precision-mode-alist*
35   (list (cons :24-bit float-precision-24-bit)
36         (cons :53-bit float-precision-53-bit)
37         (cons :64-bit float-precision-64-bit)))
38
39 ;;; Return a mask with all the specified float trap bits set.
40 (defun float-trap-mask (names)
41   (reduce #'logior
42           (mapcar (lambda (x)
43                     (or (cdr (assoc x *float-trap-alist*))
44                         (error "unknown float trap kind: ~S" x)))
45                   names)))
46 ) ; EVAL-WHEN
47
48 ;;; interpreter stubs for floating point modes get/setters for the
49 ;;; alpha have been removed to alpha-vm.lisp, as they are implemented
50 ;;; in C rather than as VOPs. Likewise for x86-64 and mips.
51 #!-(or alpha x86-64 mips)
52 (progn
53   (defun floating-point-modes ()
54     (floating-point-modes))
55   (defun (setf floating-point-modes) (new)
56     (setf (floating-point-modes) new)))
57
58 (defun set-floating-point-modes (&key
59                                  (traps nil traps-p)
60                                  (rounding-mode nil round-p)
61                                  (current-exceptions nil current-x-p)
62                                  (accrued-exceptions nil accrued-x-p)
63                                  (fast-mode nil fast-mode-p)
64                                  #!+x86 (precision nil precisionp))
65   #!+sb-doc
66   "This function sets options controlling the floating-point
67 hardware. If a keyword is not supplied, then the current value is
68 preserved. Possible keywords:
69
70  :TRAPS
71    A list of the exception conditions that should cause traps.
72    Possible exceptions are :UNDERFLOW, :OVERFLOW, :INEXACT, :INVALID,
73   :DIVIDE-BY-ZERO, and on the X86 :DENORMALIZED-OPERAND.
74
75 :ROUNDING-MODE
76    The rounding mode to use when the result is not exact. Possible
77    values are :NEAREST, :POSITIVE-INFINITY, :NEGATIVE-INFINITY and
78    :ZERO.  Setting this away from :NEAREST is liable to upset SBCL's
79    maths routines which depend on it.
80
81 :CURRENT-EXCEPTIONS
82 :ACCRUED-EXCEPTIONS
83    These arguments allow setting of the exception flags. The main
84    use is setting the accrued exceptions to NIL to clear them.
85
86 :FAST-MODE
87    Set the hardware's \"fast mode\" flag, if any. When set, IEEE
88    conformance or debuggability may be impaired. Some machines don't
89    have this feature, and some SBCL ports don't implement it anyway
90    -- in such cases the value is always NIL.
91
92 :PRECISION (x86 only)
93   :24-bit, :53-bit and :64-bit, for the internal precision of the mantissa.
94
95 GET-FLOATING-POINT-MODES may be used to find the floating point modes
96 currently in effect. SAVE-LISP-AND-DIE preserves the floating point modes
97 in effect."
98   (let ((modes (floating-point-modes)))
99     (when traps-p
100       (setf (ldb float-traps-byte modes) (float-trap-mask traps)))
101     (when round-p
102       (setf (ldb float-rounding-mode modes)
103             (or (cdr (assoc rounding-mode *rounding-mode-alist*))
104                 (error "unknown rounding mode: ~S" rounding-mode))))
105     (when current-x-p
106       (setf (ldb float-exceptions-byte modes)
107             (float-trap-mask current-exceptions)))
108     (when accrued-x-p
109       (setf (ldb float-sticky-bits modes)
110             (float-trap-mask accrued-exceptions)))
111     (when fast-mode-p
112       (if fast-mode
113           (setq modes (logior float-fast-bit modes))
114           (setq modes (logand (lognot float-fast-bit) modes))))
115     #!+x86
116     (when precisionp
117       (setf (ldb float-precision-control modes)
118             (or (cdr (assoc precision *precision-mode-alist*))
119                 (error "unknown precision mode: ~S" precision))))
120     ;; FIXME: This apparently doesn't work on Darwin
121     #!-(and darwin ppc)
122     (setf (floating-point-modes) modes))
123   (values))
124
125 (defun get-floating-point-modes ()
126   #!+sb-doc
127   "This function returns a list representing the state of the floating
128 point modes. The list is in the same format as the &KEY arguments to
129 SET-FLOATING-POINT-MODES, i.e.
130
131   (apply #'set-floating-point-modes (get-floating-point-modes))
132
133 sets the floating point modes to their current values (and thus is a no-op)."
134   (flet ((exc-keys (bits)
135            (macrolet ((frob ()
136                         `(collect ((res))
137                            ,@(mapcar (lambda (x)
138                                        `(when (logtest bits ,(cdr x))
139                                           (res ',(car x))))
140                                      *float-trap-alist*)
141                            (res))))
142              (frob))))
143     (let ((modes (floating-point-modes)))
144       `(:traps ,(exc-keys (ldb float-traps-byte modes))
145         :rounding-mode ,(car (rassoc (ldb float-rounding-mode modes)
146                                      *rounding-mode-alist*))
147         :current-exceptions ,(exc-keys (ldb float-exceptions-byte modes))
148         :accrued-exceptions ,(exc-keys (ldb float-sticky-bits modes))
149         :fast-mode ,(logtest float-fast-bit modes)
150         #!+x86 :precision
151         #!+x86 ,(car (rassoc (ldb float-precision-control modes)
152                              *precision-mode-alist*))))))
153
154 ;;; FIXME: For some unknown reason, NetBSD/x86 won't run with the
155 ;;; :INVALID trap enabled. That should be fixed, but not today...
156 ;;;
157 ;;; PRINT seems not to like x86 NPX denormal floats like
158 ;;; LEAST-NEGATIVE-SINGLE-FLOAT, so the :UNDERFLOW exceptions are
159 ;;; disabled by default. Joe User can explicitly enable them if
160 ;;; desired.
161 (defvar *saved-floating-point-modes*
162   '(:traps (:overflow #!-(or netbsd ppc) :invalid :divide-by-zero)
163     :rounding-mode :nearest :current-exceptions nil
164     :accrued-exceptions nil :fast-mode nil
165     #!+x86 :precision #!+x86 :53-bit))
166
167 (defun float-cold-init-or-reinit ()
168   (apply #'set-floating-point-modes *saved-floating-point-modes*))
169
170 (defun float-deinit ()
171   (setf *saved-floating-point-modes* (get-floating-point-modes)))
172
173 ;;; Return true if any of the named traps are currently trapped, false
174 ;;; otherwise.
175 (defmacro current-float-trap (&rest traps)
176   `(not (zerop (logand ,(dpb (float-trap-mask traps) float-traps-byte 0)
177                        (floating-point-modes)))))
178
179 ;;; SIGFPE code to floating-point error
180 #!-win32
181 (defparameter *sigfpe-code-error-alist*
182   (list (cons sb!unix::fpe-intovf 'floating-point-overflow)
183         (cons sb!unix::fpe-intdiv 'division-by-zero)
184         (cons sb!unix::fpe-fltdiv 'division-by-zero)
185         (cons sb!unix::fpe-fltovf 'floating-point-overflow)
186         (cons sb!unix::fpe-fltund 'floating-point-underflow)
187         (cons sb!unix::fpe-fltres 'floating-point-inexact)
188         (cons sb!unix::fpe-fltinv 'floating-point-invalid-operation)
189         (cons sb!unix::fpe-fltsub 'floating-point-exception)))
190
191 ;;; Signal the appropriate condition when we get a floating-point error.
192 #!-win32
193 (defun sigfpe-handler (signal info context)
194   (declare (ignore signal context))
195   (declare (type system-area-pointer info))
196   (let ((code (sb!unix::siginfo-code info)))
197     (with-interrupts
198         (error (or (cdr (assoc code *sigfpe-code-error-alist*))
199                    'floating-point-exception)))))
200
201 ;;; Execute BODY with the floating point exceptions listed in TRAPS
202 ;;; masked (disabled). TRAPS should be a list of possible exceptions
203 ;;; which includes :UNDERFLOW, :OVERFLOW, :INEXACT, :INVALID and
204 ;;; :DIVIDE-BY-ZERO and on the X86 :DENORMALIZED-OPERAND. The
205 ;;; respective accrued exceptions are cleared at the start of the body
206 ;;; to support their testing within, and restored on exit.
207 (defmacro with-float-traps-masked (traps &body body)
208   (let ((traps (dpb (float-trap-mask traps) float-traps-byte 0))
209         (exceptions (dpb (float-trap-mask traps) float-sticky-bits 0))
210         (trap-mask (dpb (lognot (float-trap-mask traps))
211                         float-traps-byte #xffffffff))
212         (exception-mask (dpb (lognot (float-trap-mask traps))
213                              float-sticky-bits #xffffffff))
214         (orig-modes (gensym)))
215     `(let ((,orig-modes (floating-point-modes)))
216       (unwind-protect
217            (progn
218              (setf (floating-point-modes)
219                    (logand ,orig-modes ,(logand trap-mask exception-mask)))
220              ,@body)
221         ;; Restore the original traps and exceptions.
222         (setf (floating-point-modes)
223               (logior (logand ,orig-modes ,(logior traps exceptions))
224                       (logand (floating-point-modes)
225                               ,(logand trap-mask exception-mask))))))))