Home | History | Annotate | Line # | Download | only in sparc64
fenv.c revision 1.1.2.2
      1 /*	$NetBSD: fenv.c,v 1.1.2.2 2011/03/06 15:07:55 bouyer 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  */
     26 #include <sys/cdefs.h>
     27 __RCSID("$NetBSD: fenv.c,v 1.1.2.2 2011/03/06 15:07:55 bouyer Exp $");
     28 
     29 #include <assert.h>
     30 #include <fenv.h>
     31 
     32 /* Load floating-point state register (all 64bits) */
     33 #define	__ldxfsr(__r)	__asm__	__volatile__		\
     34 	("ldx %0, %%fsr" : : "m" (__r))
     35 
     36 /* Save floating-point state register (all 64bits) */
     37 #define	__stxfsr(__r)	__asm__	__volatile__		\
     38 	("stx %%fsr, %0" : "=m" (*(__r)))
     39 
     40 /*
     41  * The feclearexcept() function clears the supported floating-point exceptions
     42  * represented by `excepts'.
     43  */
     44 int
     45 feclearexcept(int excepts)
     46 {
     47 	fexcept_t r;
     48 	int ex;
     49 
     50 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
     51 
     52 	ex = excepts & FE_ALL_EXCEPT;
     53 
     54 	__stxfsr(&r);
     55 	r &= ~ex;
     56 	__ldxfsr(r);
     57 
     58 	/* Success */
     59 	return 0;
     60 }
     61 
     62 /*
     63  * The fegetexceptflag() function stores an implementation-defined
     64  * representation of the states of the floating-point status flags indicated
     65  * by the argument excepts in the object pointed to by the argument flagp.
     66  */
     67 int
     68 fegetexceptflag(fexcept_t *flagp, int excepts)
     69 {
     70 	fexcept_t r;
     71 	int ex;
     72 
     73 	_DIAGASSERT(flagp != NULL);
     74 	_DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0);
     75 
     76 	ex = excepts & FE_ALL_EXCEPT;
     77 
     78 	__stxfsr(&r);
     79 	*flagp = r & ex;
     80 
     81 	/* Success */
     82 	return 0;
     83 }
     84 
     85 
     86 /*
     87  * This function sets the floating-point status flags indicated by the argument
     88  * `excepts' to the states stored in the object pointed to by `flagp'. It does
     89  * NOT raise any floating-point exceptions, but only sets the state of the flags.
     90  */
     91 int
     92 fesetexceptflag(const fexcept_t *flagp, int excepts)
     93 {
     94 	fexcept_t r;
     95 	int ex;
     96 
     97 	_DIAGASSERT(flagp != NULL);
     98 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
     99 
    100 	ex = excepts & FE_ALL_EXCEPT;
    101 
    102 	__stxfsr(&r);
    103 	r &= ~ex;
    104 	r |= *flagp & ex;
    105 	__ldxfsr(r);
    106 
    107 	/* Success */
    108 	return 0;
    109 }
    110 
    111 /*
    112  * The feraiseexcept() function raises the supported floating-point exceptions
    113  * represented by the argument `excepts'.
    114  *
    115  * The order in which these floating-point exceptions are raised is unspecified
    116  * (by the standard).
    117  */
    118 int
    119 feraiseexcept(int excepts)
    120 {
    121 	volatile double d;
    122 	int ex;
    123 
    124 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
    125 
    126 	ex = excepts & FE_ALL_EXCEPT;
    127 
    128 	/*
    129 	 * With a compiler that supports the FENV_ACCESS pragma properly, simple
    130 	 * expressions like '0.0 / 0.0' should be sufficient to generate traps.
    131 	 * Unfortunately, we need to bring a volatile variable into the equation
    132 	 * to prevent incorrect optimizations.
    133 	 */
    134 	if (ex & FE_INVALID) {
    135 		d = 0.0;
    136 		d = 0.0 / d;
    137 	}
    138 	if (ex & FE_DIVBYZERO) {
    139 		d = 0.0;
    140 		d = 1.0 / d;
    141 	}
    142 	if (ex & FE_OVERFLOW) {
    143 		d = 0x1.ffp1023;
    144 		d *= 2.0;
    145 	}
    146 	if (ex & FE_UNDERFLOW) {
    147 		d = 0x1p-1022;
    148 		d /= 0x1p1023;
    149 	}
    150 	if (ex & FE_INEXACT) {
    151 		d = 0x1p-1022;
    152 		d += 1.0;
    153 	}
    154 
    155 	/* Success */
    156 	return 0;
    157 }
    158 
    159 /*
    160  * The fetestexcept() function determines which of a specified subset of the
    161  * floating-point exception flags are currently set. The `excepts' argument
    162  * specifies the floating-point status flags to be queried.
    163  */
    164 int
    165 fetestexcept(int excepts)
    166 {
    167 	fexcept_t r;
    168 
    169 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
    170 
    171 	__stxfsr(&r);
    172 
    173 	return r & (excepts & FE_ALL_EXCEPT);
    174 }
    175 
    176 /*
    177  * The fegetround() function gets the current rounding direction.
    178  */
    179 int
    180 fegetround(void)
    181 {
    182 	fenv_t r;
    183 
    184 	__stxfsr(&r);
    185 
    186 	return (r >> _ROUND_SHIFT) & _ROUND_MASK;
    187 }
    188 
    189 /*
    190  * The fesetround() function establishes the rounding direction represented by
    191  * its argument `round'. If the argument is not equal to the value of a rounding
    192  * direction macro, the rounding direction is not changed.
    193  */
    194 int
    195 fesetround(int round)
    196 {
    197 	fenv_t r;
    198 
    199 	_DIAGASSERT((round & ~_ROUND_MASK) == 0);
    200 	if (round & ~_ROUND_MASK)
    201 		return -1;
    202 
    203 	__stxfsr(&r);
    204 	r &= ~(_ROUND_MASK << _ROUND_SHIFT);
    205 	r |= round << _ROUND_SHIFT;
    206 	__ldxfsr(r);
    207 
    208 	/* Success */
    209 	return 0;
    210 }
    211 
    212 /*
    213  * The fegetenv() function attempts to store the current floating-point
    214  * environment in the object pointed to by envp.
    215  */
    216 int
    217 fegetenv(fenv_t *envp)
    218 {
    219 	_DIAGASSERT(envp != NULL);
    220 
    221 	__stxfsr(envp);
    222 
    223 	/* Success */
    224 	return 0;
    225 }
    226 
    227 
    228 /*
    229  * The feholdexcept() function saves the current floating-point environment
    230  * in the object pointed to by envp, clears the floating-point status flags, and
    231  * then installs a non-stop (continue on floating-point exceptions) mode, if
    232  * available, for all floating-point exceptions.
    233  */
    234 int
    235 feholdexcept(fenv_t *envp)
    236 {
    237 	fenv_t r;
    238 
    239 	_DIAGASSERT(envp != NULL);
    240 
    241 	__stxfsr(&r);
    242 	*envp = r;
    243 	r &= ~(FE_ALL_EXCEPT | _ENABLE_MASK);
    244 	__ldxfsr(r);
    245 
    246 	/* Success */
    247 	return 0;
    248 }
    249 
    250 /*
    251  * The fesetenv() function attempts to establish the floating-point environment
    252  * represented by the object pointed to by envp. The argument `envp' points
    253  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
    254  * floating-point environment macro. The fesetenv() function does not raise
    255  * floating-point exceptions, but only installs the state of the floating-point
    256  * status flags represented through its argument.
    257  */
    258 int
    259 fesetenv(const fenv_t *envp)
    260 {
    261 	_DIAGASSERT(envp != NULL);
    262 
    263 	__ldxfsr(*envp);
    264 
    265 	/* Success */
    266 	return 0;
    267 }
    268 
    269 
    270 /*
    271  * The feupdateenv() function saves the currently raised floating-point
    272  * exceptions in its automatic storage, installs the floating-point environment
    273  * represented by the object pointed to by `envp', and then raises the saved
    274  * floating-point exceptions. The argument `envp' shall point to an object set
    275  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
    276  * environment macro.
    277  */
    278 int
    279 feupdateenv(const fenv_t *envp)
    280 {
    281 	fexcept_t r;
    282 
    283 	_DIAGASSERT(envp != NULL);
    284 
    285 	__stxfsr(&r);
    286 	__ldxfsr(*envp);
    287 
    288 	_DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0);
    289 	feraiseexcept(r & FE_ALL_EXCEPT);
    290 
    291 	/* Success */
    292 	return 0;
    293 }
    294 
    295 /*
    296  * The following functions are extentions to the standard
    297  */
    298 int
    299 feenableexcept(int mask)
    300 {
    301 	fenv_t old_r, new_r;
    302 
    303 	__stxfsr(&old_r);
    304 	new_r = old_r | ((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
    305 	__ldxfsr(new_r);
    306 
    307 	return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
    308 }
    309 
    310 int
    311 fedisableexcept(int mask)
    312 {
    313 	fenv_t old_r, new_r;
    314 
    315 	__stxfsr(&old_r);
    316 	new_r = old_r & ~((mask & FE_ALL_EXCEPT) << _FPUSW_SHIFT);
    317 	__ldxfsr(new_r);
    318 
    319 	return (old_r >> _FPUSW_SHIFT) & FE_ALL_EXCEPT;
    320 }
    321 
    322 int
    323 fegetexcept(void)
    324 {
    325 	fenv_t r;
    326 
    327 	__stxfsr(&r);
    328 	return (r & _ENABLE_MASK) >> _FPUSW_SHIFT;
    329 }
    330