Home | History | Annotate | Download | only in i387
      1 /*-
      2  * Copyright (c) 2004-2005 David Schultz <das (at) FreeBSD.ORG>
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     24  * SUCH DAMAGE.
     25  *
     26  * $FreeBSD: src/lib/msun/i387/fenv.c,v 1.2 2005/03/17 22:21:46 das Exp $
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 #include <sys/types.h>
     31 #include "npx.h"
     32 #include "fenv.h"
     33 
     34 const fenv_t __fe_dfl_env = {
     35 	__INITIAL_NPXCW__,
     36 	0x0000,
     37 	0x0000,
     38 	0x1f80,
     39 	0xffffffff,
     40 	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     41 	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff }
     42 };
     43 
     44 enum __sse_support __has_sse =
     45 #ifdef __SSE__
     46 	__SSE_YES;
     47 #else
     48 	__SSE_UNK;
     49 #endif
     50 
     51 #define	getfl(x)	__asm __volatile("pushfl\n\tpopl %0" : "=mr" (*(x)))
     52 #define	setfl(x)	__asm __volatile("pushl %0\n\tpopfl" : : "g" (x))
     53 #define	cpuid_dx(x)	__asm __volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t"  \
     54 					 "cpuid\n\tpopl %%ebx"		      \
     55 					: "=d" (*(x)) : : "eax", "ecx")
     56 
     57 /*
     58  * Test for SSE support on this processor.  We need to do this because
     59  * we need to use ldmxcsr/stmxcsr to get correct results if any part
     60  * of the program was compiled to use SSE floating-point, but we can't
     61  * use SSE on older processors.
     62  */
     63 int
     64 __test_sse(void)
     65 {
     66 	int flag, nflag;
     67 	int dx_features;
     68 
     69 	/* Am I a 486? */
     70 	getfl(&flag);
     71 	nflag = flag ^ 0x200000;
     72 	setfl(nflag);
     73 	getfl(&nflag);
     74 	if (flag != nflag) {
     75 		/* Not a 486, so CPUID should work. */
     76 		cpuid_dx(&dx_features);
     77 		if (dx_features & 0x2000000) {
     78 			__has_sse = __SSE_YES;
     79 			return (1);
     80 		}
     81 	}
     82 	__has_sse = __SSE_NO;
     83 	return (0);
     84 }
     85 
     86 int
     87 fesetexceptflag(const fexcept_t *flagp, int excepts)
     88 {
     89 	fenv_t env;
     90 	int mxcsr;
     91 
     92 	__fnstenv(&env);
     93 	env.__status &= ~excepts;
     94 	env.__status |= *flagp & excepts;
     95 	__fldenv(env);
     96 
     97 	if (__HAS_SSE()) {
     98 		__stmxcsr(&mxcsr);
     99 		mxcsr &= ~excepts;
    100 		mxcsr |= *flagp & excepts;
    101 		__ldmxcsr(mxcsr);
    102 	}
    103 
    104 	return (0);
    105 }
    106 
    107 int
    108 feraiseexcept(int excepts)
    109 {
    110 	fexcept_t ex = excepts;
    111 
    112 	fesetexceptflag(&ex, excepts);
    113 	__fwait();
    114 	return (0);
    115 }
    116 
    117 int
    118 fegetenv(fenv_t *envp)
    119 {
    120 	int control, mxcsr;
    121 
    122 	/*
    123 	 * fnstenv masks all exceptions, so we need to save and
    124 	 * restore the control word to avoid this side effect.
    125 	 */
    126 	__fnstcw(&control);
    127 	__fnstenv(envp);
    128 	if (__HAS_SSE()) {
    129 		__stmxcsr(&mxcsr);
    130 		__set_mxcsr(*envp, mxcsr);
    131 	}
    132 	__fldcw(control);
    133 	return (0);
    134 }
    135 
    136 int
    137 feholdexcept(fenv_t *envp)
    138 {
    139 	int mxcsr;
    140 
    141 	__fnstenv(envp);
    142 	__fnclex();
    143 	if (__HAS_SSE()) {
    144 		__stmxcsr(&mxcsr);
    145 		__set_mxcsr(*envp, mxcsr);
    146 		mxcsr &= ~FE_ALL_EXCEPT;
    147 		mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
    148 		__ldmxcsr(mxcsr);
    149 	}
    150 	return (0);
    151 }
    152 
    153 int
    154 feupdateenv(const fenv_t *envp)
    155 {
    156 	int mxcsr, status;
    157 
    158 	__fnstsw(&status);
    159 	if (__HAS_SSE())
    160 		__stmxcsr(&mxcsr);
    161 	else
    162 		mxcsr = 0;
    163 	fesetenv(envp);
    164 	feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT);
    165 	return (0);
    166 }
    167 
    168 int
    169 __feenableexcept(int mask)
    170 {
    171 	int mxcsr, control, omask;
    172 
    173 	mask &= FE_ALL_EXCEPT;
    174 	__fnstcw(&control);
    175 	if (__HAS_SSE())
    176 		__stmxcsr(&mxcsr);
    177 	else
    178 		mxcsr = 0;
    179 	omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
    180 	control &= ~mask;
    181 	__fldcw(control);
    182 	if (__HAS_SSE()) {
    183 		mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
    184 		__ldmxcsr(mxcsr);
    185 	}
    186 	return (~omask);
    187 }
    188 
    189 int
    190 __fedisableexcept(int mask)
    191 {
    192 	int mxcsr, control, omask;
    193 
    194 	mask &= FE_ALL_EXCEPT;
    195 	__fnstcw(&control);
    196 	if (__HAS_SSE())
    197 		__stmxcsr(&mxcsr);
    198 	else
    199 		mxcsr = 0;
    200 	omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
    201 	control |= mask;
    202 	__fldcw(control);
    203 	if (__HAS_SSE()) {
    204 		mxcsr |= mask << _SSE_EMASK_SHIFT;
    205 		__ldmxcsr(mxcsr);
    206 	}
    207 	return (~omask);
    208 }
    209 
    210 __weak_reference(__feenableexcept, feenableexcept);
    211 __weak_reference(__fedisableexcept, fedisableexcept);
    212