Home | History | Annotate | Line # | Download | only in x86_64
fenv.c revision 1.1
      1 /* $NetBSD: fenv.c,v 1.1 2010/07/31 21:47:53 joerg Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2004-2005 David Schultz <das (at) FreeBSD.ORG>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __RCSID("$NetBSD: fenv.c,v 1.1 2010/07/31 21:47:53 joerg Exp $");
     31 
     32 #include <assert.h>
     33 #include <fenv.h>
     34 #include <stddef.h>
     35 #include <string.h>
     36 
     37 /* Load x87 Control Word */
     38 #define	__fldcw(__cw)		__asm__ __volatile__ \
     39 	("fldcw %0" : : "m" (__cw))
     40 
     41 /* No-Wait Store Control Word */
     42 #define	__fnstcw(__cw)		__asm__ __volatile__ \
     43 	("fnstcw %0" : "=m" (*(__cw)))
     44 
     45 /* No-Wait Store Status Word */
     46 #define	__fnstsw(__sw)		__asm__ __volatile__ \
     47 	("fnstsw %0" : "=am" (*(__sw)))
     48 
     49 /* No-Wait Clear Exception Flags */
     50 #define	__fnclex()		__asm__ __volatile__ \
     51 	("fnclex")
     52 
     53 /* Load x87 Environment */
     54 #define	__fldenv(__env)		__asm__ __volatile__ \
     55 	("fldenv %0" : : "m" (__env))
     56 
     57 /* No-Wait Store x87 environment */
     58 #define	__fnstenv(__env)	__asm__ __volatile__ \
     59 	("fnstenv %0" : "=m" (*(__env)))
     60 
     61 /* Load the MXCSR register */
     62 #define	__ldmxcsr(__mxcsr)	__asm__ __volatile__ \
     63 	("ldmxcsr %0" : : "m" (__mxcsr))
     64 
     65 /* Store the MXCSR register state */
     66 #define	__stmxcsr(__mxcsr)	__asm__ __volatile__ \
     67 	("stmxcsr %0" : "=m" (*(__mxcsr)))
     68 
     69 /*
     70  * The following constant represents the default floating-point environment
     71  * (that is, the one installed at program startup) and has type pointer to
     72  * const-qualified fenv_t.
     73  *
     74  * It can be used as an argument to the functions within the <fenv.h> header
     75  * that manage the floating-point environment, namely fesetenv() and
     76  * feupdateenv().
     77  *
     78  * x87 fpu registers are 16bit wide. The upper bits, 31-16, are marked as
     79  * RESERVED. We provide a partial floating-point environment, where we
     80  * define only the lower bits. The reserved bits are extracted and set by
     81  * the consumers of FE_DFL_ENV, during runtime.
     82  */
     83 fenv_t __fe_dfl_env = {
     84 	{
     85 		__NetBSD_NPXCW__,	/* Control word register */
     86 		0x00000000,		/* Status word register */
     87 		0x0000ffff,		/* Tag word register */
     88 		{
     89 			0x00000000,
     90 			0x00000000,
     91 			0x00000000,
     92 			0x00000000,
     93 		},
     94 	},
     95 	__INITIAL_MXCSR__       /* MXCSR register */
     96 };
     97 #define FE_DFL_ENV      ((const fenv_t *) &__fe_dfl_env)
     98 
     99 
    100 /*
    101  * The feclearexcept() function clears the supported floating-point exceptions
    102  * represented by `excepts'.
    103  */
    104 int
    105 feclearexcept(int excepts)
    106 {
    107 	fenv_t fenv;
    108 	int ex;
    109 
    110 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
    111 
    112 	ex = excepts & FE_ALL_EXCEPT;
    113 
    114 	/* Store the current x87 floating-point environment */
    115 	__fnstenv(&fenv);
    116 
    117 	/* Clear the requested floating-point exceptions */
    118 	fenv.x87.status &= ~ex;
    119 
    120 	/* Load the x87 floating-point environent */
    121 	__fldenv(fenv);
    122 
    123 	/* Same for SSE environment */
    124 	__stmxcsr(&fenv.mxcsr);
    125 	fenv.mxcsr &= ~ex;
    126 	__ldmxcsr(fenv.mxcsr);
    127 
    128 	/* Success */
    129 	return (0);
    130 }
    131 
    132 /*
    133  * The fegetexceptflag() function stores an implementation-defined
    134  * representation of the states of the floating-point status flags indicated by
    135  * the argument excepts in the object pointed to by the argument flagp.
    136  */
    137 int
    138 fegetexceptflag(fexcept_t *flagp, int excepts)
    139 {
    140 	uint32_t mxcsr;
    141 	uint16_t x87_status;
    142 	int ex;
    143 
    144 	_DIAGASSERT(flagp != NULL);
    145 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
    146 
    147 	ex = excepts & FE_ALL_EXCEPT;
    148 
    149 	/* Store the current x87 status register */
    150 	__fnstsw(&x87_status);
    151 
    152 	/* Store the MXCSR register */
    153 	__stmxcsr(&mxcsr);
    154 
    155 	/* Store the results in flagp */
    156 	*flagp = (x87_status | mxcsr) & ex;
    157 
    158 	/* Success */
    159 	return (0);
    160 }
    161 
    162 /*
    163  * The feraiseexcept() function raises the supported floating-point exceptions
    164  * represented by the argument `excepts'.
    165  *
    166  * The standard explicitly allows us to execute an instruction that has the
    167  * exception as a side effect, but we choose to manipulate the status register
    168  * directly.
    169  *
    170  * The validation of input is being deferred to fesetexceptflag().
    171  */
    172 int
    173 feraiseexcept(int excepts)
    174 {
    175 	int ex;
    176 
    177 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
    178 
    179 	ex = excepts & FE_ALL_EXCEPT;
    180 	fesetexceptflag((unsigned int *)&excepts, excepts);
    181 
    182 	/* Success */
    183 	return (0);
    184 }
    185 
    186 /*
    187  * This function sets the floating-point status flags indicated by the argument
    188  * `excepts' to the states stored in the object pointed to by `flagp'. It does
    189  * NOT raise any floating-point exceptions, but only sets the state of the flags.
    190  */
    191 int
    192 fesetexceptflag(const fexcept_t *flagp, int excepts)
    193 {
    194 	fenv_t fenv;
    195 	int ex;
    196 
    197 	_DIAGASSERT(flagp != NULL);
    198 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
    199 
    200 	ex = excepts & FE_ALL_EXCEPT;
    201 
    202 	/* Store the current x87 floating-point environment */
    203 	__fnstenv(&fenv);
    204 
    205 	/* Set the requested status flags */
    206 	fenv.x87.status |= *flagp & ex;
    207 
    208 	/* Load the x87 floating-point environent */
    209 	__fldenv(fenv);
    210 
    211 	/* Same for SSE environment */
    212 	__stmxcsr(&fenv.mxcsr);
    213 	fenv.mxcsr |= *flagp & ex;
    214 	__ldmxcsr(fenv.mxcsr);
    215 
    216 	/* Success */
    217 	return (0);
    218 }
    219 
    220 /*
    221  * The fetestexcept() function determines which of a specified subset of the
    222  * floating-point exception flags are currently set. The `excepts' argument
    223  * specifies the floating-point status flags to be queried.
    224  */
    225 int
    226 fetestexcept(int excepts)
    227 {
    228 	fenv_t fenv;
    229 	uint32_t mxcsr;
    230 	uint16_t status;
    231 	int ex;
    232 
    233 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
    234 
    235 	ex = excepts & FE_ALL_EXCEPT;
    236 
    237 	/* Store the current x87 floating-point environment */
    238 	memset(&fenv, 0, sizeof(fenv));
    239 
    240 	__fnstenv(&fenv);
    241 	__fnstsw(&status);
    242 
    243 	/* Store the MXCSR register state */
    244 	__stmxcsr(&fenv.mxcsr);
    245 	__stmxcsr(&mxcsr);
    246 
    247 	return ((fenv.x87.status | fenv.mxcsr) & ex);
    248 }
    249 
    250 /*
    251  * The fegetround() function gets the current rounding direction.
    252  */
    253 int
    254 fegetround(void)
    255 {
    256 	uint32_t mxcsr;
    257 	uint16_t control;
    258 
    259 	/*
    260 	 * We check both the x87 floating-point unit _and_ the SSE unit.
    261 	 * Normally, those two must agree with respect to each other. If they
    262 	 * don't, it's not our fault and the result is non-determinable, in
    263 	 * which case POSIX says that a negative value should be returned.
    264 	 */
    265 	__fnstcw(&control);
    266 	__stmxcsr(&mxcsr);
    267 
    268 	if ((control & _X87_ROUNDING_MASK)
    269 	    != ((mxcsr & _SSE_ROUNDING_MASK) >> 3)) {
    270 		return (-1);
    271 	}
    272 
    273 	return (control & _X87_ROUNDING_MASK);
    274 }
    275 
    276 /*
    277  * The fesetround() function establishes the rounding direction represented by
    278  * its argument `round'. If the argument is not equal to the value of a rounding
    279  * direction macro, the rounding direction is not changed.
    280  */
    281 int
    282 fesetround(int round)
    283 {
    284 	uint32_t  mxcsr;
    285 	uint16_t control;
    286 
    287 	/* Check whether requested rounding direction is supported */
    288 	if (round & (~_X87_ROUNDING_MASK))
    289 		return (-1);
    290 
    291 	/* Store the current x87 control word register  */
    292 	__fnstcw(&control);
    293 
    294 	/*
    295 	 * Set the rounding direction
    296 	 * Rounding Control is bits 10-11, so shift appropriately
    297 	 */
    298 	control &= ~_X87_ROUNDING_MASK;
    299 	control |= round;
    300 
    301 	/* Load the x87 control word register */
    302 	__fldcw(control);
    303 
    304 	/*
    305 	 * Same for the SSE environment
    306 	 * Rounding Control is bits 13-14, so shift appropriately
    307 	 */
    308 	__stmxcsr(&mxcsr);
    309 	mxcsr &= ~_SSE_ROUNDING_MASK;
    310 	mxcsr |= (round << _SSE_ROUND_SHIFT);
    311 	__ldmxcsr(mxcsr);
    312 
    313 	/* Success */
    314 	return (0);
    315 }
    316 
    317 /*
    318  * The fegetenv() function attempts to store the current floating-point
    319  * environment in the object pointed to by envp.
    320  */
    321 int
    322 fegetenv(fenv_t *envp)
    323 {
    324 	_DIAGASSERT(envp != NULL);
    325 
    326 	/* Store the current x87 floating-point environment */
    327 	__fnstenv(envp);
    328 
    329 	/* Store the MXCSR register state */
    330 	__stmxcsr(&envp->mxcsr);
    331 
    332      /*
    333       * When an FNSTENV instruction is executed, all pending exceptions are
    334       * essentially lost (either the x87 FPU status register is cleared or all
    335       * exceptions are masked).
    336       *
    337       * 8.6 X87 FPU EXCEPTION SYNCHRONIZATION -
    338       * Intel(R) 64 and IA-32 Architectures Softare Developer's Manual - Vol 1
    339       *
    340       */
    341 	__fldcw(envp->x87.control);
    342 
    343 	/* Success */
    344 	return (0);
    345 }
    346 
    347 /*
    348  * The feholdexcept() function saves the current floating-point environment
    349  * in the object pointed to by envp, clears the floating-point status flags, and
    350  * then installs a non-stop (continue on floating-point exceptions) mode, if
    351  * available, for all floating-point exceptions.
    352  */
    353 int
    354 feholdexcept(fenv_t *envp)
    355 {
    356 	uint32_t mxcsr;
    357 
    358 	_DIAGASSERT(envp != NULL);
    359 
    360 	/* Store the current x87 floating-point environment */
    361 	__fnstenv(envp);
    362 
    363 	/* Clear all exception flags in FPU */
    364 	__fnclex();
    365 
    366 	/* Store the MXCSR register state */
    367 	__stmxcsr(&envp->mxcsr);
    368 
    369 	/* Clear exception flags in MXCSR XXX */
    370 	mxcsr = envp->mxcsr;
    371 	mxcsr &= ~FE_ALL_EXCEPT;
    372 
    373 	/* Mask all exceptions */
    374 	mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT;
    375 
    376 	__ldmxcsr(mxcsr);
    377 
    378 	/* Success */
    379 	return (0);
    380 }
    381 
    382 /*
    383  * The fesetenv() function attempts to establish the floating-point environment
    384  * represented by the object pointed to by envp. The argument `envp' points
    385  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
    386  * floating-point environment macro. The fesetenv() function does not raise
    387  * floating-point exceptions, but only installs the state of the floating-point
    388  * status flags represented through its argument.
    389  */
    390 int
    391 fesetenv(const fenv_t *envp)
    392 {
    393 	fenv_t fenv;
    394 
    395 	_DIAGASSERT(envp != NULL);
    396 
    397 	/* Store the x87 floating-point environment */
    398 	memset(&fenv, 0, sizeof fenv);
    399 	__fnstenv(&fenv);
    400 
    401 	__fe_dfl_env.x87.control = (fenv.x87.control & 0xffff0000)
    402 	    | (__fe_dfl_env.x87.control & 0x0000ffff);
    403 	__fe_dfl_env.x87.status = (fenv.x87.status & 0xffff0000)
    404 	    | (__fe_dfl_env.x87.status & 0x0000ffff);
    405 	__fe_dfl_env.x87.tag = (fenv.x87.tag & 0xffff0000)
    406 	    | (__fe_dfl_env.x87.tag & 0x0000ffff);
    407 	__fe_dfl_env.x87.others[3] = (fenv.x87.others[3] & 0xffff0000)
    408 	    | (__fe_dfl_env.x87.others[3] & 0x0000ffff);
    409 	__fldenv(*envp);
    410 
    411 	/* Store the MXCSR register */
    412 	__ldmxcsr(envp->mxcsr);
    413 
    414 	/* Success */
    415 	return (0);
    416 }
    417 
    418 /*
    419  * The feupdateenv() function saves the currently raised floating-point
    420  * exceptions in its automatic storage, installs the floating-point environment
    421  * represented by the object pointed to by `envp', and then raises the saved
    422  * floating-point exceptions. The argument `envp' shall point to an object set
    423  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
    424  * environment macro.
    425  */
    426 int
    427 feupdateenv(const fenv_t *envp)
    428 {
    429 	fenv_t fenv;
    430 	uint32_t mxcsr;
    431 	uint16_t sw;
    432 
    433 	_DIAGASSERT(envp != NULL);
    434 
    435 	/* Store the x87 floating-point environment */
    436 	memset(&fenv, 0, sizeof(fenv));
    437 	__fnstenv(&fenv);
    438 
    439 	__fe_dfl_env.x87.control = (fenv.x87.control & 0xffff0000)
    440 	    | (__fe_dfl_env.x87.control & 0x0000ffff);
    441 	__fe_dfl_env.x87.status = (fenv.x87.status & 0xffff0000)
    442 	    | (__fe_dfl_env.x87.status & 0x0000ffff);
    443 	__fe_dfl_env.x87.tag = (fenv.x87.tag & 0xffff0000)
    444 	    | (__fe_dfl_env.x87.tag & 0x0000ffff);
    445 	__fe_dfl_env.x87.others[3] = (fenv.x87.others[3] & 0xffff0000)
    446 	    | (__fe_dfl_env.x87.others[3] & 0x0000ffff);
    447 
    448 	/* Store the x87 status register */
    449 	__fnstsw(&sw);
    450 
    451 	/* Store the MXCSR register */
    452 	__stmxcsr(&mxcsr);
    453 
    454 	/* Install new floating-point environment */
    455 	fesetenv(envp);
    456 
    457 	/* Raise any previously accumulated exceptions */
    458 	feraiseexcept((sw | mxcsr) & FE_ALL_EXCEPT);
    459 
    460 	/* Success */
    461 	return (0);
    462 }
    463 
    464 /*
    465  * The following functions are extentions to the standard
    466  */
    467 int
    468 feenableexcept(int mask)
    469 {
    470 	uint32_t mxcsr, omask;
    471 	uint16_t control;
    472 
    473 	_DIAGASSERT((mask & ~FE_ALL_EXCEPT) == 0);
    474 	mask &= FE_ALL_EXCEPT;
    475 
    476 	__fnstcw(&control);
    477 	__stmxcsr(&mxcsr);
    478 
    479 	omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
    480 	control &= ~mask;
    481 	__fldcw(control);
    482 
    483 	mxcsr &= ~(mask << _SSE_EMASK_SHIFT);
    484 	__ldmxcsr(mxcsr);
    485 
    486 	return (~omask);
    487 
    488 }
    489 
    490 int
    491 fedisableexcept(int mask)
    492 {
    493 	uint32_t mxcsr, omask;
    494 	uint16_t control;
    495 
    496 	_DIAGASSERT((mask & ~FE_ALL_EXCEPT) == 0);
    497 
    498 	__fnstcw(&control);
    499 	__stmxcsr(&mxcsr);
    500 
    501 	omask = (control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT;
    502 	control |= mask;
    503 	__fldcw(control);
    504 
    505 	mxcsr |= mask << _SSE_EMASK_SHIFT;
    506 	__ldmxcsr(mxcsr);
    507 
    508 	return (~omask);
    509 }
    510 
    511 int
    512 fegetexcept(void)
    513 {
    514 	uint16_t control;
    515 
    516 	/*
    517 	 * We assume that the masks for the x87 and the SSE unit are
    518 	 * the same.
    519 	 */
    520 	__fnstcw(&control);
    521 
    522 	return (control & FE_ALL_EXCEPT);
    523 }
    524 
    525