1130144Sdas/*- 2143769Sdas * Copyright (c) 2004-2005 David Schultz <das@FreeBSD.ORG> 3130144Sdas * All rights reserved. 4130144Sdas * 5130144Sdas * Redistribution and use in source and binary forms, with or without 6130144Sdas * modification, are permitted provided that the following conditions 7130144Sdas * are met: 8130144Sdas * 1. Redistributions of source code must retain the above copyright 9130144Sdas * notice, this list of conditions and the following disclaimer. 10130144Sdas * 2. Redistributions in binary form must reproduce the above copyright 11130144Sdas * notice, this list of conditions and the following disclaimer in the 12130144Sdas * documentation and/or other materials provided with the distribution. 13130144Sdas * 14130144Sdas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15130144Sdas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16130144Sdas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17130144Sdas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18130144Sdas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19130144Sdas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20130144Sdas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21130144Sdas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22130144Sdas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23130144Sdas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24130144Sdas * SUCH DAMAGE. 25130144Sdas * 26130144Sdas * $FreeBSD: stable/11/lib/msun/i387/fenv.c 334771 2018-06-07 09:03:42Z dim $ 27130144Sdas */ 28130144Sdas 29143769Sdas#include <sys/cdefs.h> 30130144Sdas#include <sys/types.h> 31130144Sdas#include <machine/npx.h> 32130144Sdas 33226218Sdas#define __fenv_static 34226415Sdas#include "fenv.h" 35226218Sdas 36226218Sdas#ifdef __GNUC_GNU_INLINE__ 37226218Sdas#error "This file must be compiled with C99 'inline' semantics" 38226218Sdas#endif 39226218Sdas 40130144Sdasconst fenv_t __fe_dfl_env = { 41143769Sdas __INITIAL_NPXCW__, 42143769Sdas 0x0000, 43143769Sdas 0x0000, 44143769Sdas 0x1f80, 45130144Sdas 0xffffffff, 46130144Sdas { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47130144Sdas 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff } 48130144Sdas}; 49143769Sdas 50143769Sdasenum __sse_support __has_sse = 51143769Sdas#ifdef __SSE__ 52143769Sdas __SSE_YES; 53143769Sdas#else 54143769Sdas __SSE_UNK; 55143769Sdas#endif 56143769Sdas 57143769Sdas#define getfl(x) __asm __volatile("pushfl\n\tpopl %0" : "=mr" (*(x))) 58143769Sdas#define setfl(x) __asm __volatile("pushl %0\n\tpopfl" : : "g" (x)) 59143769Sdas#define cpuid_dx(x) __asm __volatile("pushl %%ebx\n\tmovl $1, %%eax\n\t" \ 60143769Sdas "cpuid\n\tpopl %%ebx" \ 61143769Sdas : "=d" (*(x)) : : "eax", "ecx") 62143769Sdas 63143769Sdas/* 64143769Sdas * Test for SSE support on this processor. We need to do this because 65143769Sdas * we need to use ldmxcsr/stmxcsr to get correct results if any part 66143769Sdas * of the program was compiled to use SSE floating-point, but we can't 67143769Sdas * use SSE on older processors. 68143769Sdas */ 69143769Sdasint 70143769Sdas__test_sse(void) 71143769Sdas{ 72143769Sdas int flag, nflag; 73143769Sdas int dx_features; 74143769Sdas 75143769Sdas /* Am I a 486? */ 76143769Sdas getfl(&flag); 77143769Sdas nflag = flag ^ 0x200000; 78143769Sdas setfl(nflag); 79143769Sdas getfl(&nflag); 80143769Sdas if (flag != nflag) { 81143769Sdas /* Not a 486, so CPUID should work. */ 82143769Sdas cpuid_dx(&dx_features); 83143769Sdas if (dx_features & 0x2000000) { 84143769Sdas __has_sse = __SSE_YES; 85143769Sdas return (1); 86143769Sdas } 87143769Sdas } 88143769Sdas __has_sse = __SSE_NO; 89143769Sdas return (0); 90143769Sdas} 91143769Sdas 92226218Sdasextern inline int feclearexcept(int __excepts); 93226218Sdasextern inline int fegetexceptflag(fexcept_t *__flagp, int __excepts); 94226218Sdas 95143769Sdasint 96143769Sdasfesetexceptflag(const fexcept_t *flagp, int excepts) 97143769Sdas{ 98143769Sdas fenv_t env; 99203441Skib __uint32_t mxcsr; 100143769Sdas 101143769Sdas __fnstenv(&env); 102143769Sdas env.__status &= ~excepts; 103143769Sdas env.__status |= *flagp & excepts; 104334771Sdim __fldenv(&env); 105143769Sdas 106143769Sdas if (__HAS_SSE()) { 107143769Sdas __stmxcsr(&mxcsr); 108143769Sdas mxcsr &= ~excepts; 109143769Sdas mxcsr |= *flagp & excepts; 110334771Sdim __ldmxcsr(&mxcsr); 111143769Sdas } 112143769Sdas 113143769Sdas return (0); 114143769Sdas} 115143769Sdas 116143769Sdasint 117143769Sdasferaiseexcept(int excepts) 118143769Sdas{ 119143769Sdas fexcept_t ex = excepts; 120143769Sdas 121143769Sdas fesetexceptflag(&ex, excepts); 122143769Sdas __fwait(); 123143769Sdas return (0); 124143769Sdas} 125143769Sdas 126226218Sdasextern inline int fetestexcept(int __excepts); 127226218Sdasextern inline int fegetround(void); 128226218Sdasextern inline int fesetround(int __round); 129226218Sdas 130143769Sdasint 131143769Sdasfegetenv(fenv_t *envp) 132143769Sdas{ 133203441Skib __uint32_t mxcsr; 134143769Sdas 135165795Sdas __fnstenv(envp); 136143769Sdas /* 137165795Sdas * fnstenv masks all exceptions, so we need to restore 138165795Sdas * the old control word to avoid this side effect. 139143769Sdas */ 140334771Sdim __fldcw(&envp->__control); 141143769Sdas if (__HAS_SSE()) { 142143769Sdas __stmxcsr(&mxcsr); 143143769Sdas __set_mxcsr(*envp, mxcsr); 144143769Sdas } 145143769Sdas return (0); 146143769Sdas} 147143769Sdas 148143769Sdasint 149143769Sdasfeholdexcept(fenv_t *envp) 150143769Sdas{ 151203441Skib __uint32_t mxcsr; 152143769Sdas 153143769Sdas __fnstenv(envp); 154143769Sdas __fnclex(); 155143769Sdas if (__HAS_SSE()) { 156143769Sdas __stmxcsr(&mxcsr); 157143769Sdas __set_mxcsr(*envp, mxcsr); 158143769Sdas mxcsr &= ~FE_ALL_EXCEPT; 159143769Sdas mxcsr |= FE_ALL_EXCEPT << _SSE_EMASK_SHIFT; 160334771Sdim __ldmxcsr(&mxcsr); 161143769Sdas } 162143769Sdas return (0); 163143769Sdas} 164143769Sdas 165226218Sdasextern inline int fesetenv(const fenv_t *__envp); 166226218Sdas 167143769Sdasint 168143769Sdasfeupdateenv(const fenv_t *envp) 169143769Sdas{ 170203441Skib __uint32_t mxcsr; 171203441Skib __uint16_t status; 172143769Sdas 173143769Sdas __fnstsw(&status); 174143769Sdas if (__HAS_SSE()) 175143769Sdas __stmxcsr(&mxcsr); 176143769Sdas else 177143769Sdas mxcsr = 0; 178143769Sdas fesetenv(envp); 179143769Sdas feraiseexcept((mxcsr | status) & FE_ALL_EXCEPT); 180143769Sdas return (0); 181143769Sdas} 182143769Sdas 183143769Sdasint 184143769Sdas__feenableexcept(int mask) 185143769Sdas{ 186203441Skib __uint32_t mxcsr, omask; 187203441Skib __uint16_t control; 188143769Sdas 189143769Sdas mask &= FE_ALL_EXCEPT; 190143769Sdas __fnstcw(&control); 191143769Sdas if (__HAS_SSE()) 192143769Sdas __stmxcsr(&mxcsr); 193143769Sdas else 194143769Sdas mxcsr = 0; 195226594Sdas omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT; 196143769Sdas control &= ~mask; 197334771Sdim __fldcw(&control); 198143769Sdas if (__HAS_SSE()) { 199143769Sdas mxcsr &= ~(mask << _SSE_EMASK_SHIFT); 200334771Sdim __ldmxcsr(&mxcsr); 201143769Sdas } 202226594Sdas return (omask); 203143769Sdas} 204143769Sdas 205143769Sdasint 206143769Sdas__fedisableexcept(int mask) 207143769Sdas{ 208203441Skib __uint32_t mxcsr, omask; 209203441Skib __uint16_t control; 210143769Sdas 211143769Sdas mask &= FE_ALL_EXCEPT; 212143769Sdas __fnstcw(&control); 213143769Sdas if (__HAS_SSE()) 214143769Sdas __stmxcsr(&mxcsr); 215143769Sdas else 216143769Sdas mxcsr = 0; 217226594Sdas omask = ~(control | mxcsr >> _SSE_EMASK_SHIFT) & FE_ALL_EXCEPT; 218143769Sdas control |= mask; 219334771Sdim __fldcw(&control); 220143769Sdas if (__HAS_SSE()) { 221143769Sdas mxcsr |= mask << _SSE_EMASK_SHIFT; 222334771Sdim __ldmxcsr(&mxcsr); 223143769Sdas } 224226594Sdas return (omask); 225143769Sdas} 226226372Sdas 227226372Sdas__weak_reference(__feenableexcept, feenableexcept); 228226372Sdas__weak_reference(__fedisableexcept, fedisableexcept); 229