1826549e5SKonstantin Belousov /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 35e53a4f9SPedro F. Giffuni * 4826549e5SKonstantin Belousov * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG> 5826549e5SKonstantin Belousov * All rights reserved. 6826549e5SKonstantin Belousov * 7826549e5SKonstantin Belousov * Redistribution and use in source and binary forms, with or without 8826549e5SKonstantin Belousov * modification, are permitted provided that the following conditions 9826549e5SKonstantin Belousov * are met: 10826549e5SKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 11826549e5SKonstantin Belousov * notice, this list of conditions and the following disclaimer. 12826549e5SKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 13826549e5SKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 14826549e5SKonstantin Belousov * documentation and/or other materials provided with the distribution. 15826549e5SKonstantin Belousov * 16826549e5SKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17826549e5SKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18826549e5SKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19826549e5SKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20826549e5SKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21826549e5SKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22826549e5SKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23826549e5SKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24826549e5SKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25826549e5SKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26826549e5SKonstantin Belousov * SUCH DAMAGE. 27826549e5SKonstantin Belousov * 28826549e5SKonstantin Belousov * $FreeBSD$ 29826549e5SKonstantin Belousov */ 30826549e5SKonstantin Belousov 31826549e5SKonstantin Belousov #ifndef _FENV_H_ 32826549e5SKonstantin Belousov #define _FENV_H_ 33826549e5SKonstantin Belousov 34826549e5SKonstantin Belousov #include <sys/cdefs.h> 35826549e5SKonstantin Belousov #include <sys/_types.h> 36b451efbeSDimitry Andric #include <ieeefp.h> 37826549e5SKonstantin Belousov 38826549e5SKonstantin Belousov #ifndef __fenv_static 39826549e5SKonstantin Belousov #define __fenv_static static 40826549e5SKonstantin Belousov #endif 41826549e5SKonstantin Belousov 42826549e5SKonstantin Belousov typedef __uint16_t fexcept_t; 43826549e5SKonstantin Belousov 44826549e5SKonstantin Belousov /* Exception flags */ 45826549e5SKonstantin Belousov #define FE_INVALID 0x01 46826549e5SKonstantin Belousov #define FE_DENORMAL 0x02 47826549e5SKonstantin Belousov #define FE_DIVBYZERO 0x04 48826549e5SKonstantin Belousov #define FE_OVERFLOW 0x08 49826549e5SKonstantin Belousov #define FE_UNDERFLOW 0x10 50826549e5SKonstantin Belousov #define FE_INEXACT 0x20 51826549e5SKonstantin Belousov #define FE_ALL_EXCEPT (FE_DIVBYZERO | FE_DENORMAL | FE_INEXACT | \ 52826549e5SKonstantin Belousov FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW) 53826549e5SKonstantin Belousov 54826549e5SKonstantin Belousov /* Rounding modes */ 55826549e5SKonstantin Belousov #define FE_TONEAREST 0x0000 56826549e5SKonstantin Belousov #define FE_DOWNWARD 0x0400 57826549e5SKonstantin Belousov #define FE_UPWARD 0x0800 58826549e5SKonstantin Belousov #define FE_TOWARDZERO 0x0c00 59826549e5SKonstantin Belousov #define _ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | \ 60826549e5SKonstantin Belousov FE_UPWARD | FE_TOWARDZERO) 61826549e5SKonstantin Belousov 62826549e5SKonstantin Belousov /* 63826549e5SKonstantin Belousov * As compared to the x87 control word, the SSE unit's control word 64826549e5SKonstantin Belousov * has the rounding control bits offset by 3 and the exception mask 65826549e5SKonstantin Belousov * bits offset by 7. 66826549e5SKonstantin Belousov */ 67826549e5SKonstantin Belousov #define _SSE_ROUND_SHIFT 3 68826549e5SKonstantin Belousov #define _SSE_EMASK_SHIFT 7 69826549e5SKonstantin Belousov 70826549e5SKonstantin Belousov #ifdef __i386__ 71826549e5SKonstantin Belousov /* 72826549e5SKonstantin Belousov * To preserve binary compatibility with FreeBSD 5.3, we pack the 73826549e5SKonstantin Belousov * mxcsr into some reserved fields, rather than changing sizeof(fenv_t). 74826549e5SKonstantin Belousov */ 75826549e5SKonstantin Belousov typedef struct { 76826549e5SKonstantin Belousov __uint16_t __control; 77826549e5SKonstantin Belousov __uint16_t __mxcsr_hi; 78826549e5SKonstantin Belousov __uint16_t __status; 79826549e5SKonstantin Belousov __uint16_t __mxcsr_lo; 80826549e5SKonstantin Belousov __uint32_t __tag; 81826549e5SKonstantin Belousov char __other[16]; 82826549e5SKonstantin Belousov } fenv_t; 83826549e5SKonstantin Belousov #else /* __amd64__ */ 84826549e5SKonstantin Belousov typedef struct { 85826549e5SKonstantin Belousov struct { 86826549e5SKonstantin Belousov __uint32_t __control; 87826549e5SKonstantin Belousov __uint32_t __status; 88826549e5SKonstantin Belousov __uint32_t __tag; 89826549e5SKonstantin Belousov char __other[16]; 90826549e5SKonstantin Belousov } __x87; 91826549e5SKonstantin Belousov __uint32_t __mxcsr; 92826549e5SKonstantin Belousov } fenv_t; 93826549e5SKonstantin Belousov #endif /* __i386__ */ 94826549e5SKonstantin Belousov 95826549e5SKonstantin Belousov __BEGIN_DECLS 96826549e5SKonstantin Belousov 97826549e5SKonstantin Belousov /* Default floating-point environment */ 98826549e5SKonstantin Belousov extern const fenv_t __fe_dfl_env; 99826549e5SKonstantin Belousov #define FE_DFL_ENV (&__fe_dfl_env) 100826549e5SKonstantin Belousov 101826549e5SKonstantin Belousov #define __fldenvx(__env) __asm __volatile("fldenv %0" : : "m" (__env) \ 102826549e5SKonstantin Belousov : "st", "st(1)", "st(2)", "st(3)", "st(4)", \ 103826549e5SKonstantin Belousov "st(5)", "st(6)", "st(7)") 104826549e5SKonstantin Belousov #define __fwait() __asm __volatile("fwait") 105826549e5SKonstantin Belousov 106826549e5SKonstantin Belousov int fegetenv(fenv_t *__envp); 107826549e5SKonstantin Belousov int feholdexcept(fenv_t *__envp); 108826549e5SKonstantin Belousov int fesetexceptflag(const fexcept_t *__flagp, int __excepts); 109826549e5SKonstantin Belousov int feraiseexcept(int __excepts); 110826549e5SKonstantin Belousov int feupdateenv(const fenv_t *__envp); 111826549e5SKonstantin Belousov 112826549e5SKonstantin Belousov __fenv_static inline int 113826549e5SKonstantin Belousov fegetround(void) 114826549e5SKonstantin Belousov { 115826549e5SKonstantin Belousov __uint16_t __control; 116826549e5SKonstantin Belousov 117826549e5SKonstantin Belousov /* 118826549e5SKonstantin Belousov * We assume that the x87 and the SSE unit agree on the 119826549e5SKonstantin Belousov * rounding mode. Reading the control word on the x87 turns 120826549e5SKonstantin Belousov * out to be about 5 times faster than reading it on the SSE 121826549e5SKonstantin Belousov * unit on an Opteron 244. 122826549e5SKonstantin Belousov */ 123826549e5SKonstantin Belousov __fnstcw(&__control); 124826549e5SKonstantin Belousov return (__control & _ROUND_MASK); 125826549e5SKonstantin Belousov } 126826549e5SKonstantin Belousov 127826549e5SKonstantin Belousov #if __BSD_VISIBLE 128826549e5SKonstantin Belousov 129826549e5SKonstantin Belousov int feenableexcept(int __mask); 130826549e5SKonstantin Belousov int fedisableexcept(int __mask); 131826549e5SKonstantin Belousov 132826549e5SKonstantin Belousov /* We currently provide no external definition of fegetexcept(). */ 133826549e5SKonstantin Belousov static inline int 134826549e5SKonstantin Belousov fegetexcept(void) 135826549e5SKonstantin Belousov { 136826549e5SKonstantin Belousov __uint16_t __control; 137826549e5SKonstantin Belousov 138826549e5SKonstantin Belousov /* 139826549e5SKonstantin Belousov * We assume that the masks for the x87 and the SSE unit are 140826549e5SKonstantin Belousov * the same. 141826549e5SKonstantin Belousov */ 142826549e5SKonstantin Belousov __fnstcw(&__control); 143826549e5SKonstantin Belousov return (~__control & FE_ALL_EXCEPT); 144826549e5SKonstantin Belousov } 145826549e5SKonstantin Belousov 146826549e5SKonstantin Belousov #endif /* __BSD_VISIBLE */ 147826549e5SKonstantin Belousov 148826549e5SKonstantin Belousov #ifdef __i386__ 149826549e5SKonstantin Belousov 150826549e5SKonstantin Belousov /* After testing for SSE support once, we cache the result in __has_sse. */ 151826549e5SKonstantin Belousov enum __sse_support { __SSE_YES, __SSE_NO, __SSE_UNK }; 152826549e5SKonstantin Belousov extern enum __sse_support __has_sse; 153826549e5SKonstantin Belousov int __test_sse(void); 154826549e5SKonstantin Belousov #ifdef __SSE__ 155826549e5SKonstantin Belousov #define __HAS_SSE() 1 156826549e5SKonstantin Belousov #else 157826549e5SKonstantin Belousov #define __HAS_SSE() (__has_sse == __SSE_YES || \ 158826549e5SKonstantin Belousov (__has_sse == __SSE_UNK && __test_sse())) 159826549e5SKonstantin Belousov #endif 160826549e5SKonstantin Belousov 161826549e5SKonstantin Belousov #define __get_mxcsr(env) (((env).__mxcsr_hi << 16) | \ 162826549e5SKonstantin Belousov ((env).__mxcsr_lo)) 163826549e5SKonstantin Belousov #define __set_mxcsr(env, x) do { \ 164826549e5SKonstantin Belousov (env).__mxcsr_hi = (__uint32_t)(x) >> 16; \ 165826549e5SKonstantin Belousov (env).__mxcsr_lo = (__uint16_t)(x); \ 166826549e5SKonstantin Belousov } while (0) 167826549e5SKonstantin Belousov 168826549e5SKonstantin Belousov __fenv_static inline int 169826549e5SKonstantin Belousov feclearexcept(int __excepts) 170826549e5SKonstantin Belousov { 171826549e5SKonstantin Belousov fenv_t __env; 172826549e5SKonstantin Belousov __uint32_t __mxcsr; 173826549e5SKonstantin Belousov 174826549e5SKonstantin Belousov if (__excepts == FE_ALL_EXCEPT) { 175826549e5SKonstantin Belousov __fnclex(); 176826549e5SKonstantin Belousov } else { 177826549e5SKonstantin Belousov __fnstenv(&__env); 178826549e5SKonstantin Belousov __env.__status &= ~__excepts; 179b451efbeSDimitry Andric __fldenv(&__env); 180826549e5SKonstantin Belousov } 181826549e5SKonstantin Belousov if (__HAS_SSE()) { 182826549e5SKonstantin Belousov __stmxcsr(&__mxcsr); 183826549e5SKonstantin Belousov __mxcsr &= ~__excepts; 184b451efbeSDimitry Andric __ldmxcsr(&__mxcsr); 185826549e5SKonstantin Belousov } 186826549e5SKonstantin Belousov return (0); 187826549e5SKonstantin Belousov } 188826549e5SKonstantin Belousov 189826549e5SKonstantin Belousov __fenv_static inline int 190826549e5SKonstantin Belousov fegetexceptflag(fexcept_t *__flagp, int __excepts) 191826549e5SKonstantin Belousov { 192826549e5SKonstantin Belousov __uint32_t __mxcsr; 193826549e5SKonstantin Belousov __uint16_t __status; 194826549e5SKonstantin Belousov 195826549e5SKonstantin Belousov __fnstsw(&__status); 196826549e5SKonstantin Belousov if (__HAS_SSE()) 197826549e5SKonstantin Belousov __stmxcsr(&__mxcsr); 198826549e5SKonstantin Belousov else 199826549e5SKonstantin Belousov __mxcsr = 0; 200826549e5SKonstantin Belousov *__flagp = (__mxcsr | __status) & __excepts; 201826549e5SKonstantin Belousov return (0); 202826549e5SKonstantin Belousov } 203826549e5SKonstantin Belousov 204826549e5SKonstantin Belousov __fenv_static inline int 205826549e5SKonstantin Belousov fetestexcept(int __excepts) 206826549e5SKonstantin Belousov { 207826549e5SKonstantin Belousov __uint32_t __mxcsr; 208826549e5SKonstantin Belousov __uint16_t __status; 209826549e5SKonstantin Belousov 210826549e5SKonstantin Belousov __fnstsw(&__status); 211826549e5SKonstantin Belousov if (__HAS_SSE()) 212826549e5SKonstantin Belousov __stmxcsr(&__mxcsr); 213826549e5SKonstantin Belousov else 214826549e5SKonstantin Belousov __mxcsr = 0; 215826549e5SKonstantin Belousov return ((__status | __mxcsr) & __excepts); 216826549e5SKonstantin Belousov } 217826549e5SKonstantin Belousov 218826549e5SKonstantin Belousov __fenv_static inline int 219826549e5SKonstantin Belousov fesetround(int __round) 220826549e5SKonstantin Belousov { 221826549e5SKonstantin Belousov __uint32_t __mxcsr; 222826549e5SKonstantin Belousov __uint16_t __control; 223826549e5SKonstantin Belousov 224826549e5SKonstantin Belousov if (__round & ~_ROUND_MASK) 225826549e5SKonstantin Belousov return (-1); 226826549e5SKonstantin Belousov 227826549e5SKonstantin Belousov __fnstcw(&__control); 228826549e5SKonstantin Belousov __control &= ~_ROUND_MASK; 229826549e5SKonstantin Belousov __control |= __round; 230b451efbeSDimitry Andric __fldcw(&__control); 231826549e5SKonstantin Belousov 232826549e5SKonstantin Belousov if (__HAS_SSE()) { 233826549e5SKonstantin Belousov __stmxcsr(&__mxcsr); 234826549e5SKonstantin Belousov __mxcsr &= ~(_ROUND_MASK << _SSE_ROUND_SHIFT); 235826549e5SKonstantin Belousov __mxcsr |= __round << _SSE_ROUND_SHIFT; 236b451efbeSDimitry Andric __ldmxcsr(&__mxcsr); 237826549e5SKonstantin Belousov } 238826549e5SKonstantin Belousov 239826549e5SKonstantin Belousov return (0); 240826549e5SKonstantin Belousov } 241826549e5SKonstantin Belousov 242826549e5SKonstantin Belousov __fenv_static inline int 243826549e5SKonstantin Belousov fesetenv(const fenv_t *__envp) 244826549e5SKonstantin Belousov { 245826549e5SKonstantin Belousov fenv_t __env = *__envp; 246826549e5SKonstantin Belousov __uint32_t __mxcsr; 247826549e5SKonstantin Belousov 248826549e5SKonstantin Belousov __mxcsr = __get_mxcsr(__env); 249826549e5SKonstantin Belousov __set_mxcsr(__env, 0xffffffff); 250826549e5SKonstantin Belousov /* 251826549e5SKonstantin Belousov * XXX Using fldenvx() instead of fldenv() tells the compiler that this 252826549e5SKonstantin Belousov * instruction clobbers the i387 register stack. This happens because 253826549e5SKonstantin Belousov * we restore the tag word from the saved environment. Normally, this 254826549e5SKonstantin Belousov * would happen anyway and we wouldn't care, because the ABI allows 255826549e5SKonstantin Belousov * function calls to clobber the i387 regs. However, fesetenv() is 256826549e5SKonstantin Belousov * inlined, so we need to be more careful. 257826549e5SKonstantin Belousov */ 258826549e5SKonstantin Belousov __fldenvx(__env); 259826549e5SKonstantin Belousov if (__HAS_SSE()) 260b451efbeSDimitry Andric __ldmxcsr(&__mxcsr); 261826549e5SKonstantin Belousov return (0); 262826549e5SKonstantin Belousov } 263826549e5SKonstantin Belousov 264826549e5SKonstantin Belousov #else /* __amd64__ */ 265826549e5SKonstantin Belousov 266826549e5SKonstantin Belousov __fenv_static inline int 267826549e5SKonstantin Belousov feclearexcept(int __excepts) 268826549e5SKonstantin Belousov { 269826549e5SKonstantin Belousov fenv_t __env; 270826549e5SKonstantin Belousov 271826549e5SKonstantin Belousov if (__excepts == FE_ALL_EXCEPT) { 272826549e5SKonstantin Belousov __fnclex(); 273826549e5SKonstantin Belousov } else { 274826549e5SKonstantin Belousov __fnstenv(&__env.__x87); 275826549e5SKonstantin Belousov __env.__x87.__status &= ~__excepts; 276b451efbeSDimitry Andric __fldenv(&__env.__x87); 277826549e5SKonstantin Belousov } 278826549e5SKonstantin Belousov __stmxcsr(&__env.__mxcsr); 279826549e5SKonstantin Belousov __env.__mxcsr &= ~__excepts; 280b451efbeSDimitry Andric __ldmxcsr(&__env.__mxcsr); 281826549e5SKonstantin Belousov return (0); 282826549e5SKonstantin Belousov } 283826549e5SKonstantin Belousov 284826549e5SKonstantin Belousov __fenv_static inline int 285826549e5SKonstantin Belousov fegetexceptflag(fexcept_t *__flagp, int __excepts) 286826549e5SKonstantin Belousov { 287826549e5SKonstantin Belousov __uint32_t __mxcsr; 288826549e5SKonstantin Belousov __uint16_t __status; 289826549e5SKonstantin Belousov 290826549e5SKonstantin Belousov __stmxcsr(&__mxcsr); 291826549e5SKonstantin Belousov __fnstsw(&__status); 292826549e5SKonstantin Belousov *__flagp = (__mxcsr | __status) & __excepts; 293826549e5SKonstantin Belousov return (0); 294826549e5SKonstantin Belousov } 295826549e5SKonstantin Belousov 296826549e5SKonstantin Belousov __fenv_static inline int 297826549e5SKonstantin Belousov fetestexcept(int __excepts) 298826549e5SKonstantin Belousov { 299826549e5SKonstantin Belousov __uint32_t __mxcsr; 300826549e5SKonstantin Belousov __uint16_t __status; 301826549e5SKonstantin Belousov 302826549e5SKonstantin Belousov __stmxcsr(&__mxcsr); 303826549e5SKonstantin Belousov __fnstsw(&__status); 304826549e5SKonstantin Belousov return ((__status | __mxcsr) & __excepts); 305826549e5SKonstantin Belousov } 306826549e5SKonstantin Belousov 307826549e5SKonstantin Belousov __fenv_static inline int 308826549e5SKonstantin Belousov fesetround(int __round) 309826549e5SKonstantin Belousov { 310826549e5SKonstantin Belousov __uint32_t __mxcsr; 311826549e5SKonstantin Belousov __uint16_t __control; 312826549e5SKonstantin Belousov 313826549e5SKonstantin Belousov if (__round & ~_ROUND_MASK) 314826549e5SKonstantin Belousov return (-1); 315826549e5SKonstantin Belousov 316826549e5SKonstantin Belousov __fnstcw(&__control); 317826549e5SKonstantin Belousov __control &= ~_ROUND_MASK; 318826549e5SKonstantin Belousov __control |= __round; 319b451efbeSDimitry Andric __fldcw(&__control); 320826549e5SKonstantin Belousov 321826549e5SKonstantin Belousov __stmxcsr(&__mxcsr); 322826549e5SKonstantin Belousov __mxcsr &= ~(_ROUND_MASK << _SSE_ROUND_SHIFT); 323826549e5SKonstantin Belousov __mxcsr |= __round << _SSE_ROUND_SHIFT; 324b451efbeSDimitry Andric __ldmxcsr(&__mxcsr); 325826549e5SKonstantin Belousov 326826549e5SKonstantin Belousov return (0); 327826549e5SKonstantin Belousov } 328826549e5SKonstantin Belousov 329826549e5SKonstantin Belousov __fenv_static inline int 330826549e5SKonstantin Belousov fesetenv(const fenv_t *__envp) 331826549e5SKonstantin Belousov { 332826549e5SKonstantin Belousov 333826549e5SKonstantin Belousov /* 334826549e5SKonstantin Belousov * XXX Using fldenvx() instead of fldenv() tells the compiler that this 335826549e5SKonstantin Belousov * instruction clobbers the i387 register stack. This happens because 336826549e5SKonstantin Belousov * we restore the tag word from the saved environment. Normally, this 337826549e5SKonstantin Belousov * would happen anyway and we wouldn't care, because the ABI allows 338826549e5SKonstantin Belousov * function calls to clobber the i387 regs. However, fesetenv() is 339826549e5SKonstantin Belousov * inlined, so we need to be more careful. 340826549e5SKonstantin Belousov */ 341826549e5SKonstantin Belousov __fldenvx(__envp->__x87); 342b451efbeSDimitry Andric __ldmxcsr(&__envp->__mxcsr); 343826549e5SKonstantin Belousov return (0); 344826549e5SKonstantin Belousov } 345826549e5SKonstantin Belousov 346826549e5SKonstantin Belousov #endif /* __i386__ */ 347826549e5SKonstantin Belousov 348826549e5SKonstantin Belousov __END_DECLS 349826549e5SKonstantin Belousov 350826549e5SKonstantin Belousov #endif /* !_FENV_H_ */ 351