Home | History | Annotate | Line # | Download | only in hppa
fenv.c revision 1.3.4.1
      1  1.3.4.1  bouyer /*	$NetBSD: fenv.c,v 1.3.4.1 2017/04/21 16:53:10 bouyer Exp $	*/
      2      1.1  martin 
      3      1.1  martin /*-
      4      1.1  martin  * Copyright (c) 2004-2005 David Schultz <das (at) FreeBSD.ORG>
      5      1.1  martin  * All rights reserved.
      6      1.1  martin  *
      7      1.1  martin  * Redistribution and use in source and binary forms, with or without
      8      1.1  martin  * modification, are permitted provided that the following conditions
      9      1.1  martin  * are met:
     10      1.1  martin  * 1. Redistributions of source code must retain the above copyright
     11      1.1  martin  *    notice, this list of conditions and the following disclaimer.
     12      1.1  martin  * 2. Redistributions in binary form must reproduce the above copyright
     13      1.1  martin  *    notice, this list of conditions and the following disclaimer in the
     14      1.1  martin  *    documentation and/or other materials provided with the distribution.
     15      1.1  martin  *
     16      1.1  martin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17      1.1  martin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18      1.1  martin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19      1.1  martin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20      1.1  martin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21      1.1  martin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22      1.1  martin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23      1.1  martin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24      1.1  martin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25      1.1  martin  */
     26      1.1  martin #include <sys/cdefs.h>
     27  1.3.4.1  bouyer __RCSID("$NetBSD: fenv.c,v 1.3.4.1 2017/04/21 16:53:10 bouyer Exp $");
     28  1.3.4.1  bouyer 
     29  1.3.4.1  bouyer #include "namespace.h"
     30      1.1  martin 
     31      1.1  martin #include <assert.h>
     32      1.1  martin #include <fenv.h>
     33      1.1  martin 
     34  1.3.4.1  bouyer #ifdef __weak_alias
     35  1.3.4.1  bouyer __weak_alias(feclearexcept,_feclearexcept)
     36  1.3.4.1  bouyer __weak_alias(fedisableexcept,_fedisableexcept)
     37  1.3.4.1  bouyer __weak_alias(feenableexcept,_feenableexcept)
     38  1.3.4.1  bouyer __weak_alias(fegetenv,_fegetenv)
     39  1.3.4.1  bouyer __weak_alias(fegetexcept,_fegetexcept)
     40  1.3.4.1  bouyer __weak_alias(fegetexceptflag,_fegetexceptflag)
     41  1.3.4.1  bouyer __weak_alias(fegetround,_fegetround)
     42  1.3.4.1  bouyer __weak_alias(feholdexcept,_feholdexcept)
     43  1.3.4.1  bouyer __weak_alias(feraiseexcept,_feraiseexcept)
     44  1.3.4.1  bouyer __weak_alias(fesetenv,_fesetenv)
     45  1.3.4.1  bouyer __weak_alias(fesetexceptflag,_fesetexceptflag)
     46  1.3.4.1  bouyer __weak_alias(fesetround,_fesetround)
     47  1.3.4.1  bouyer __weak_alias(fetestexcept,_fetestexcept)
     48  1.3.4.1  bouyer __weak_alias(feupdateenv,_feupdateenv)
     49  1.3.4.1  bouyer #endif
     50  1.3.4.1  bouyer 
     51      1.1  martin /*
     52      1.1  martin  * Convert from exception flags (__BITS(27,32)) to exception enable bits
     53      1.1  martin  * (__BITS(5,0)) by right-shifting this much:
     54      1.1  martin  */
     55      1.1  martin #define	FE_FLAGS_SHIFT		27
     56      1.1  martin 
     57      1.1  martin /*
     58      1.1  martin  * Mask all rounding mode bits
     59      1.1  martin  */
     60      1.1  martin #define FE_ROUND_MASK	(FE_TONEAREST | FE_DOWNWARD | \
     61      1.1  martin 			FE_UPWARD | FE_TOWARDZERO)
     62      1.1  martin 
     63      1.1  martin /* Load lower 32 bits from floating-point state register */
     64      1.1  martin static inline uint32_t
     65      1.1  martin readfpsr(void)
     66      1.1  martin {
     67      1.1  martin 	uint32_t rv;
     68      1.1  martin 
     69      1.3   skrll 	__asm__	__volatile__ ("fstw	%%fr0, 0(%1)" : "=m" (rv) : "r"(&rv));
     70      1.1  martin 	return rv;
     71      1.1  martin }
     72      1.1  martin 
     73      1.1  martin /* Save floating-point state register */
     74      1.1  martin static inline void
     75      1.1  martin writefpsr(uint32_t val)
     76      1.1  martin {
     77      1.3   skrll 	__asm__	__volatile__("fldw	0(%1),%%fr0" : : "m" (val), "r"(&val));
     78      1.1  martin }
     79      1.1  martin 
     80      1.1  martin /*
     81      1.1  martin  * The feclearexcept() function clears the supported floating-point exceptions
     82      1.1  martin  * represented by `excepts'.
     83      1.1  martin  */
     84      1.1  martin int
     85      1.1  martin feclearexcept(int excepts)
     86      1.1  martin {
     87      1.1  martin 	fexcept_t r;
     88      1.1  martin 	int ex;
     89      1.1  martin 
     90      1.1  martin 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
     91      1.1  martin 
     92      1.1  martin 	ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
     93      1.1  martin 
     94      1.1  martin 	r = readfpsr();
     95      1.1  martin 	r &= ~ex;
     96      1.1  martin 	writefpsr(r);
     97      1.1  martin 
     98      1.1  martin 	/* Success */
     99      1.1  martin 	return 0;
    100      1.1  martin }
    101      1.1  martin 
    102      1.1  martin /*
    103      1.1  martin  * The fegetexceptflag() function stores an implementation-defined
    104      1.1  martin  * representation of the states of the floating-point status flags indicated
    105      1.1  martin  * by the argument excepts in the object pointed to by the argument flagp.
    106      1.1  martin  */
    107      1.1  martin int
    108      1.1  martin fegetexceptflag(fexcept_t *flagp, int excepts)
    109      1.1  martin {
    110      1.1  martin 	fexcept_t r;
    111      1.1  martin 	int ex;
    112      1.1  martin 
    113      1.1  martin 	_DIAGASSERT(flagp != NULL);
    114      1.1  martin 	_DIAGASSERT((excepts & ~_FE_ALL_EXCEPT) == 0);
    115      1.1  martin 
    116      1.1  martin 	ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
    117      1.1  martin 
    118      1.1  martin 	r = readfpsr();
    119      1.1  martin 	*flagp = (r & ex) >> FE_FLAGS_SHIFT;
    120      1.1  martin 
    121      1.1  martin 	/* Success */
    122      1.1  martin 	return 0;
    123      1.1  martin }
    124      1.1  martin 
    125      1.1  martin 
    126      1.1  martin /*
    127      1.1  martin  * This function sets the floating-point status flags indicated by the argument
    128      1.1  martin  * `excepts' to the states stored in the object pointed to by `flagp'. It does
    129      1.1  martin  * NOT raise any floating-point exceptions, but only sets the state of the flags.
    130      1.1  martin  */
    131      1.1  martin int
    132      1.1  martin fesetexceptflag(const fexcept_t *flagp, int excepts)
    133      1.1  martin {
    134      1.1  martin 	fexcept_t r;
    135      1.1  martin 	int ex;
    136      1.1  martin 
    137      1.1  martin 	_DIAGASSERT(flagp != NULL);
    138      1.1  martin 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
    139      1.1  martin 
    140      1.1  martin 	ex = (excepts & FE_ALL_EXCEPT) << FE_FLAGS_SHIFT;
    141      1.1  martin 
    142      1.1  martin 	r = readfpsr();
    143      1.1  martin 	r &= ~ex;
    144      1.1  martin 	r |= (*flagp << FE_FLAGS_SHIFT) & ex;
    145      1.1  martin 	writefpsr(r);
    146      1.1  martin 
    147      1.1  martin 	/* Success */
    148      1.1  martin 	return 0;
    149      1.1  martin }
    150      1.1  martin 
    151      1.1  martin /*
    152      1.1  martin  * The feraiseexcept() function raises the supported floating-point exceptions
    153      1.1  martin  * represented by the argument `excepts'.
    154      1.1  martin  *
    155      1.1  martin  * The order in which these floating-point exceptions are raised is unspecified
    156      1.1  martin  * (by the standard).
    157      1.1  martin  */
    158      1.1  martin int
    159      1.1  martin feraiseexcept(int excepts)
    160      1.1  martin {
    161      1.1  martin 	volatile double d;
    162      1.1  martin 	int ex;
    163      1.1  martin 
    164      1.1  martin 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
    165      1.1  martin 
    166      1.1  martin 	ex = excepts & FE_ALL_EXCEPT;
    167      1.1  martin 
    168      1.1  martin 	/*
    169      1.1  martin 	 * With a compiler that supports the FENV_ACCESS pragma properly, simple
    170      1.1  martin 	 * expressions like '0.0 / 0.0' should be sufficient to generate traps.
    171      1.1  martin 	 * Unfortunately, we need to bring a volatile variable into the equation
    172      1.1  martin 	 * to prevent incorrect optimizations.
    173      1.1  martin 	 */
    174      1.1  martin 	if (ex & FE_INVALID) {
    175      1.1  martin 		d = 0.0;
    176      1.1  martin 		d = 0.0 / d;
    177      1.1  martin 	}
    178      1.1  martin 	if (ex & FE_DIVBYZERO) {
    179      1.1  martin 		d = 0.0;
    180      1.1  martin 		d = 1.0 / d;
    181      1.1  martin 	}
    182      1.1  martin 	if (ex & FE_OVERFLOW) {
    183      1.1  martin 		d = 0x1.ffp1023;
    184      1.1  martin 		d *= 2.0;
    185      1.1  martin 	}
    186      1.1  martin 	if (ex & FE_UNDERFLOW) {
    187      1.1  martin 		d = 0x1p-1022;
    188      1.1  martin 		d /= 0x1p1023;
    189      1.1  martin 	}
    190      1.1  martin 	if (ex & FE_INEXACT) {
    191      1.1  martin 		d = 0x1p-1022;
    192      1.1  martin 		d += 1.0;
    193      1.1  martin 	}
    194      1.1  martin 
    195      1.1  martin 	/* Success */
    196      1.1  martin 	return 0;
    197      1.1  martin }
    198      1.1  martin 
    199      1.1  martin /*
    200      1.1  martin  * The fetestexcept() function determines which of a specified subset of the
    201      1.1  martin  * floating-point exception flags are currently set. The `excepts' argument
    202      1.1  martin  * specifies the floating-point status flags to be queried.
    203      1.1  martin  */
    204      1.1  martin int
    205      1.1  martin fetestexcept(int excepts)
    206      1.1  martin {
    207      1.1  martin 	fexcept_t r;
    208      1.1  martin 
    209      1.1  martin 	_DIAGASSERT((excepts & ~FE_ALL_EXCEPT) == 0);
    210      1.1  martin 
    211      1.1  martin 	r = readfpsr();
    212      1.1  martin 
    213      1.1  martin 	return (r >> FE_FLAGS_SHIFT) & (excepts & FE_ALL_EXCEPT);
    214      1.1  martin }
    215      1.1  martin 
    216      1.1  martin /*
    217      1.1  martin  * The fegetround() function gets the current rounding direction.
    218      1.1  martin  */
    219      1.1  martin int
    220      1.1  martin fegetround(void)
    221      1.1  martin {
    222      1.1  martin 	fenv_t r;
    223      1.1  martin 
    224      1.1  martin 	r = readfpsr();
    225      1.1  martin 
    226      1.2  martin 	return r & FE_ROUND_MASK;
    227      1.1  martin }
    228      1.1  martin 
    229      1.1  martin /*
    230      1.1  martin  * The fesetround() function establishes the rounding direction represented by
    231      1.1  martin  * its argument `round'. If the argument is not equal to the value of a rounding
    232      1.1  martin  * direction macro, the rounding direction is not changed.
    233      1.1  martin  */
    234      1.1  martin int
    235      1.1  martin fesetround(int round)
    236      1.1  martin {
    237      1.1  martin 	fenv_t r;
    238      1.1  martin 
    239      1.1  martin 	_DIAGASSERT((round & ~FE_ROUND_MASK) == 0);
    240      1.1  martin 	if (round & ~FE_ROUND_MASK)
    241      1.1  martin 		return -1;
    242      1.1  martin 
    243      1.1  martin 	r = readfpsr();
    244      1.2  martin 	r &= ~FE_ROUND_MASK;
    245      1.2  martin 	r |= round;
    246      1.1  martin 	writefpsr(r);
    247      1.1  martin 
    248      1.1  martin 	/* Success */
    249      1.1  martin 	return 0;
    250      1.1  martin }
    251      1.1  martin 
    252      1.1  martin /*
    253      1.1  martin  * The fegetenv() function attempts to store the current floating-point
    254      1.1  martin  * environment in the object pointed to by envp.
    255      1.1  martin  */
    256      1.1  martin int
    257      1.1  martin fegetenv(fenv_t *envp)
    258      1.1  martin {
    259      1.1  martin 	_DIAGASSERT(envp != NULL);
    260      1.1  martin 
    261      1.1  martin 	*envp = readfpsr();
    262      1.1  martin 
    263      1.1  martin 	/* Success */
    264      1.1  martin 	return 0;
    265      1.1  martin }
    266      1.1  martin 
    267      1.1  martin 
    268      1.1  martin /*
    269      1.1  martin  * The feholdexcept() function saves the current floating-point environment
    270      1.1  martin  * in the object pointed to by envp, clears the floating-point status flags, and
    271      1.1  martin  * then installs a non-stop (continue on floating-point exceptions) mode, if
    272      1.1  martin  * available, for all floating-point exceptions.
    273      1.1  martin  */
    274      1.1  martin int
    275      1.1  martin feholdexcept(fenv_t *envp)
    276      1.1  martin {
    277      1.1  martin 	fenv_t r;
    278      1.1  martin 
    279      1.1  martin 	_DIAGASSERT(envp != NULL);
    280      1.1  martin 
    281      1.1  martin 	r = readfpsr();
    282      1.1  martin 	*envp = r;
    283      1.1  martin 	r &= ~FE_ALL_EXCEPT;
    284      1.1  martin 	writefpsr(r);
    285      1.1  martin 
    286      1.1  martin 	/* Success */
    287      1.1  martin 	return 0;
    288      1.1  martin }
    289      1.1  martin 
    290      1.1  martin /*
    291      1.1  martin  * The fesetenv() function attempts to establish the floating-point environment
    292      1.1  martin  * represented by the object pointed to by envp. The argument `envp' points
    293      1.1  martin  * to an object set by a call to fegetenv() or feholdexcept(), or equal a
    294      1.1  martin  * floating-point environment macro. The fesetenv() function does not raise
    295      1.1  martin  * floating-point exceptions, but only installs the state of the floating-point
    296      1.1  martin  * status flags represented through its argument.
    297      1.1  martin  */
    298      1.1  martin int
    299      1.1  martin fesetenv(const fenv_t *envp)
    300      1.1  martin {
    301      1.1  martin 	_DIAGASSERT(envp != NULL);
    302      1.1  martin 
    303      1.1  martin 	writefpsr(*envp);
    304      1.1  martin 
    305      1.1  martin 	/* Success */
    306      1.1  martin 	return 0;
    307      1.1  martin }
    308      1.1  martin 
    309      1.1  martin 
    310      1.1  martin /*
    311      1.1  martin  * The feupdateenv() function saves the currently raised floating-point
    312      1.1  martin  * exceptions in its automatic storage, installs the floating-point environment
    313      1.1  martin  * represented by the object pointed to by `envp', and then raises the saved
    314      1.1  martin  * floating-point exceptions. The argument `envp' shall point to an object set
    315      1.1  martin  * by a call to feholdexcept() or fegetenv(), or equal a floating-point
    316      1.1  martin  * environment macro.
    317      1.1  martin  */
    318      1.1  martin int
    319      1.1  martin feupdateenv(const fenv_t *envp)
    320      1.1  martin {
    321      1.1  martin 	fexcept_t r;
    322      1.1  martin 
    323      1.1  martin 	_DIAGASSERT(envp != NULL);
    324      1.1  martin 
    325      1.1  martin 	r = readfpsr();
    326      1.1  martin 	writefpsr(*envp);
    327      1.1  martin 
    328      1.1  martin 	_DIAGASSERT((r & ~FE_ALL_EXCEPT) == 0);
    329      1.1  martin 	feraiseexcept(r & FE_ALL_EXCEPT);
    330      1.1  martin 
    331      1.1  martin 	/* Success */
    332      1.1  martin 	return 0;
    333      1.1  martin }
    334      1.1  martin 
    335      1.1  martin /*
    336      1.1  martin  * The following functions are extentions to the standard
    337      1.1  martin  */
    338      1.1  martin int
    339      1.1  martin feenableexcept(int mask)
    340      1.1  martin {
    341      1.1  martin 	fenv_t old_r, new_r;
    342      1.1  martin 
    343      1.1  martin 	old_r = readfpsr();
    344      1.1  martin 	new_r = old_r | (mask & FE_ALL_EXCEPT);
    345      1.1  martin 	writefpsr(new_r);
    346      1.1  martin 
    347      1.1  martin 	return old_r & FE_ALL_EXCEPT;
    348      1.1  martin }
    349      1.1  martin 
    350      1.1  martin int
    351      1.1  martin fedisableexcept(int mask)
    352      1.1  martin {
    353      1.1  martin 	fenv_t old_r, new_r;
    354      1.1  martin 
    355      1.1  martin 	old_r = readfpsr();
    356      1.1  martin 	new_r = old_r & ~(mask & FE_ALL_EXCEPT);
    357      1.1  martin 	writefpsr(new_r);
    358      1.1  martin 
    359      1.1  martin 	return old_r & FE_ALL_EXCEPT;
    360      1.1  martin }
    361      1.1  martin 
    362      1.1  martin int
    363      1.1  martin fegetexcept(void)
    364      1.1  martin {
    365      1.1  martin 	fenv_t r;
    366      1.1  martin 
    367      1.1  martin 	r = readfpsr();
    368      1.1  martin 	return r & FE_ALL_EXCEPT;
    369      1.1  martin }
    370