diff --git a/src/code/float-trap.lisp b/src/code/float-trap.lisp index 7f27ffdc6be517cef816163128415eef4553da8b..d97d04e82326ce6375d113dba879c32113f8d3c2 100644 --- a/src/code/float-trap.lisp +++ b/src/code/float-trap.lisp @@ -64,17 +64,62 @@ (defun (setf floating-point-modes) (new) (setf (floating-point-modes) new)) ) +#+(and x86 (not sse2)) +(progn + (defun floating-point-modes () + (let ((x87-modes (vm::x87-floating-point-modes))) + ;; Massage the bits from x87-floating-point-modes into the order + ;; that the rest of the system wants them to be. (Must match + ;; format in the SSE2 mxcsr register.) + (logior (ash (logand #x3f x87-modes) 7) ; control + (logand #x3f (ash x87-modes -16))))) + (defun (setf floating-point-modes) (new) + (let* ((rc (ldb float-rounding-mode new)) + (x87-modes + (logior (ash (logand #x3f new) 16) + (ash rc 10) + (logand #x3f (ash new -7)) + ;; Set precision control to be 53-bit, always. + ;; (The compiler takes care of handling + ;; single-float precision, and we don't support + ;; long-floats.) + (ash 2 8)))) + (setf (x87-floating-point-modes) x87-modes))) + ) + #+sse2 (progn (defun floating-point-modes () - ;; Get just the SSE2 mode bits. - (vm::sse2-floating-point-modes)) + ;; Combine the modes from the FPU and SSE2 units. Since the sse + ;; mode contains all of the common information we want, we massage + ;; the x87-modes to match, and then OR the x87 and sse2 modes + ;; together. Note: We ignore the rounding control bits from the + ;; FPU and only use the SSE2 rounding control bits. + (let* ((x87-modes (vm::x87-floating-point-modes)) + (sse-modes (vm::sse2-floating-point-modes)) + (final-mode (logior sse-modes + (ash (logand #x3f x87-modes) 7) ; control + (logand #x3f (ash x87-modes -16))))) + + final-mode)) (defun (setf floating-point-modes) (new-mode) (declare (type (unsigned-byte 24) new-mode)) - ;; Set the floating point modes for SSE2. - (setf (vm::sse2-floating-point-modes) new-mode) + ;; Set the floating point modes for both X87 and SSE2. This + ;; include the rounding control bits. + (let* ((rc (ldb float-rounding-mode new-mode)) + (x87-modes + (logior (ash (logand #x3f new-mode) 16) + (ash rc 10) + (logand #x3f (ash new-mode -7)) + ;; Set precision control to be 64-bit, always. We + ;; don't use the x87 registers with sse2, so this + ;; is ok and would be the correct setting if we + ;; ever support long-floats. + (ash 3 8)))) + (setf (vm::sse2-floating-point-modes) new-mode) + (setf (vm::x87-floating-point-modes) x87-modes)) new-mode) - ) +) ;;; SET-FLOATING-POINT-MODES -- Public ;;; diff --git a/src/compiler/x86/float.lisp b/src/compiler/x86/float.lisp index a515e402801d9fe347d6bf861975c3dbb05705e4..e61714948aea0f439dc4ca274344dfeb67dce8fe 100644 --- a/src/compiler/x86/float.lisp +++ b/src/compiler/x86/float.lisp @@ -2303,6 +2303,43 @@ (defknown ((setf x87-floating-point-modes)) (float-modes) float-modes) +;; For the record, here is the format of the x86 FPU status word +;; +;; Bit +;; 15 FPU Busy +;; 14 C3 (condition code) +;; 13-11 Top of stack +;; 10 C2 (condition code) +;; 9 C1 (condition code) +;; 8 C0 (condition code) +;; 7 Error summary status +;; 6 Stack fault +;; 5 precision flag (inexact) +;; 4 underflow flag +;; 3 overflow flag +;; 2 divide-by-zero flag +;; 1 denormalized operand flag +;; 0 invalid operation flag +;; +;; When one of the flag bits (0-5) is set, then that exception has +;; been detected since the bits were last cleared. +;; +;; The control word: +;; +;; 15-13 reserved +;; 12 infinity control +;; 11-10 rounding control +;; 9-8 precision control +;; 7-6 reserved +;; 5 precision masked +;; 4 underflow masked +;; 3 overflow masked +;; 2 divide-by-zero masked +;; 1 denormal operand masked +;; 0 invalid operation masked +;; +;; When one of the mask bits (0-5) is set, then that exception is +;; masked so that no exception is generated. (define-vop (x87-floating-point-modes) (:results (res :scs (unsigned-reg))) (:result-types unsigned-num) diff --git a/src/lisp/Linux-os.c b/src/lisp/Linux-os.c index 2da60fb59791eb574879c8eb2bca02a00ee2077e..7f7a4d7acd350c1ef5b443ef5a28cf848c2e368f 100644 --- a/src/lisp/Linux-os.c +++ b/src/lisp/Linux-os.c @@ -217,13 +217,16 @@ os_sigcontext_fpu_reg(ucontext_t *scp, int offset) if (fpregs) { if (offset < 8) { reg = (unsigned char *) &fpregs->_st[offset]; - } else if (offset < 16) { + } +#ifdef FEATURE_SSE2 + else { struct _fpstate *fpstate; fpstate = (struct _fpstate*) scp->uc_mcontext.fpregs; if (fpstate->magic != 0xffff) { reg = (unsigned char *) &fpstate->_xmm[offset - 8]; } } +#endif } return reg; } @@ -231,27 +234,39 @@ os_sigcontext_fpu_reg(ucontext_t *scp, int offset) unsigned int os_sigcontext_fpu_modes(ucontext_t *scp) { - unsigned int modes = 0; - - /* - * Get the SSE2 modes. FIXME: What should we do if the magic - * value indicates that the mxcsr value is not in the context? - */ - struct _fpstate *fpstate; - unsigned long mxcsr; + unsigned int modes; + unsigned short cw, sw; - fpstate = (struct _fpstate*) scp->uc_mcontext.fpregs; - if (fpstate->magic == 0xffff) { - mxcsr = 0; + if (scp->uc_mcontext.fpregs == NULL) { + cw = 0; + sw = 0x3f; } else { - mxcsr = fpstate->mxcsr; - DPRINTF(0, (stderr, "SSE2 modes = %08lx\n", mxcsr)); + cw = scp->uc_mcontext.fpregs->cw & 0xffff; + sw = scp->uc_mcontext.fpregs->sw & 0xffff; } - modes |= mxcsr; + modes = ((cw & 0x3f) << 7) | (sw & 0x3f); + +#ifdef FEATURE_SSE2 + /* + * Add in the SSE2 part, if we're running the sse2 core. + */ + if (fpu_mode == SSE2) { + struct _fpstate *fpstate; + unsigned long mxcsr; + + fpstate = (struct _fpstate*) scp->uc_mcontext.fpregs; + if (fpstate->magic == 0xffff) { + mxcsr = 0; + } else { + mxcsr = fpstate->mxcsr; + DPRINTF(0, (stderr, "SSE2 modes = %08lx\n", mxcsr)); + } + modes |= mxcsr; + } +#endif - /* Convert exception mask to exception enable */ modes ^= (0x3f << 7); return modes; } @@ -528,19 +543,25 @@ void restore_fpu(ucontext_t *context) { if (context->uc_mcontext.fpregs) { - struct _fpstate *fpstate; - unsigned int mxcsr; + short cw = context->uc_mcontext.fpregs->cw; + DPRINTF(0, (stderr, "restore_fpu: cw = %08x\n", cw)); + __asm__ __volatile__ ("fldcw %0" : : "m" (*&cw)); +#ifdef FEATURE_SSE2 + if (fpu_mode == SSE2) { + struct _fpstate *fpstate; + unsigned int mxcsr; - fpstate = (struct _fpstate*) context->uc_mcontext.fpregs; - if (fpstate->magic != 0xffff) { - mxcsr = fpstate->mxcsr; - DPRINTF(0, (stderr, "restore_fpu: mxcsr (raw) = %04x\n", mxcsr)); - __asm__ __volatile__ ("ldmxcsr %0" :: "m" (*&mxcsr)); + fpstate = (struct _fpstate*) context->uc_mcontext.fpregs; + if (fpstate->magic != 0xffff) { + mxcsr = fpstate->mxcsr; + DPRINTF(0, (stderr, "restore_fpu: mxcsr (raw) = %04x\n", mxcsr)); + __asm__ __volatile__ ("ldmxcsr %0" :: "m" (*&mxcsr)); + } } +#endif } } - #ifdef i386 boolean os_support_sse2()