Serenity Operating System
at master 256 lines 5.4 kB view raw
1/* 2 * Copyright (c) 2021, Mițca Dumitru <dumitru0mitca@gmail.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Types.h> 8#include <fenv.h> 9 10// This is the size of the floating point environment image in protected mode 11static_assert(sizeof(__x87_floating_point_environment) == 28); 12 13#ifndef AK_ARCH_AARCH64 14static u16 read_status_register() 15{ 16 u16 status_register; 17 asm volatile("fnstsw %0" 18 : "=m"(status_register)); 19 return status_register; 20} 21 22static u16 read_control_word() 23{ 24 u16 control_word; 25 asm volatile("fnstcw %0" 26 : "=m"(control_word)); 27 return control_word; 28} 29 30static void set_control_word(u16 new_control_word) 31{ 32 asm volatile("fldcw %0" ::"m"(new_control_word)); 33} 34 35static u32 read_mxcsr() 36{ 37 u32 mxcsr; 38 asm volatile("stmxcsr %0" 39 : "=m"(mxcsr)); 40 return mxcsr; 41} 42 43static void set_mxcsr(u32 new_mxcsr) 44{ 45 asm volatile("ldmxcsr %0" ::"m"(new_mxcsr)); 46} 47 48static constexpr u32 default_mxcsr_value = 0x1f80; 49#endif 50 51extern "C" { 52 53int fegetenv(fenv_t* env) 54{ 55 if (!env) 56 return 1; 57 58#ifdef AK_ARCH_AARCH64 59 (void)env; 60 TODO_AARCH64(); 61#else 62 asm volatile("fnstenv %0" 63 : "=m"(env->__x87_fpu_env)::"memory"); 64 65 env->__mxcsr = read_mxcsr(); 66#endif 67 68 return 0; 69} 70 71int fesetenv(fenv_t const* env) 72{ 73 if (!env) 74 return 1; 75 76#ifdef AK_ARCH_AARCH64 77 (void)env; 78 TODO_AARCH64(); 79#else 80 if (env == FE_DFL_ENV) { 81 asm volatile("finit"); 82 set_mxcsr(default_mxcsr_value); 83 return 0; 84 } 85 86 asm volatile("fldenv %0" ::"m"(env->__x87_fpu_env) 87 : "memory"); 88 89 set_mxcsr(env->__mxcsr); 90#endif 91 92 return 0; 93} 94 95int feholdexcept(fenv_t* env) 96{ 97 fegetenv(env); 98 99 fenv_t current_env; 100 fegetenv(&current_env); 101 102#ifdef AK_ARCH_AARCH64 103 (void)env; 104 TODO_AARCH64(); 105#else 106 current_env.__x87_fpu_env.__status_word &= ~FE_ALL_EXCEPT; 107 current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit 108 current_env.__x87_fpu_env.__control_word &= FE_ALL_EXCEPT; // Masking these bits stops the corresponding exceptions from being generated according to the Intel Programmer's Manual 109#endif 110 111 fesetenv(&current_env); 112 113 return 0; 114} 115 116int feupdateenv(fenv_t const* env) 117{ 118 auto currently_raised_exceptions = fetestexcept(FE_ALL_EXCEPT); 119 120 fesetenv(env); 121 feraiseexcept(currently_raised_exceptions); 122 123 return 0; 124} 125 126int fegetexceptflag(fexcept_t* except, int exceptions) 127{ 128 if (!except) 129 return 1; 130 *except = (uint16_t)fetestexcept(exceptions); 131 return 0; 132} 133int fesetexceptflag(fexcept_t const* except, int exceptions) 134{ 135 if (!except) 136 return 1; 137 138 fenv_t current_env; 139 fegetenv(&current_env); 140 141 exceptions &= FE_ALL_EXCEPT; 142#ifdef AK_ARCH_AARCH64 143 (void)exceptions; 144 (void)except; 145 TODO_AARCH64(); 146#else 147 current_env.__x87_fpu_env.__status_word &= exceptions; 148 current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Make sure exceptions don't get raised 149#endif 150 151 fesetenv(&current_env); 152 return 0; 153} 154 155int fegetround() 156{ 157#ifdef AK_ARCH_AARCH64 158 TODO_AARCH64(); 159#else 160 // There's no way to signal whether the SSE rounding mode and x87 ones are different, so we assume they're the same 161 return (read_status_register() >> 10) & 3; 162#endif 163} 164 165int fesetround(int rounding_mode) 166{ 167 if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOWARDZERO) 168 return 1; 169 170#ifdef AK_ARCH_AARCH64 171 TODO_AARCH64(); 172#else 173 auto control_word = read_control_word(); 174 175 control_word &= ~(3 << 10); 176 control_word |= rounding_mode << 10; 177 178 set_control_word(control_word); 179 180 auto mxcsr = read_mxcsr(); 181 182 mxcsr &= ~(3 << 13); 183 mxcsr |= rounding_mode << 13; 184 185 set_mxcsr(mxcsr); 186 187#endif 188 189 return 0; 190} 191 192int feclearexcept(int exceptions) 193{ 194 exceptions &= FE_ALL_EXCEPT; 195 196 fenv_t current_env; 197 fegetenv(&current_env); 198 199#ifdef AK_ARCH_AARCH64 200 (void)exceptions; 201 TODO_AARCH64(); 202#else 203 current_env.__x87_fpu_env.__status_word &= ~exceptions; 204 current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit 205#endif 206 207 fesetenv(&current_env); 208 return 0; 209} 210 211int fetestexcept(int exceptions) 212{ 213#ifdef AK_ARCH_AARCH64 214 (void)exceptions; 215 TODO_AARCH64(); 216#else 217 u16 status_register = read_status_register() & FE_ALL_EXCEPT; 218 exceptions &= FE_ALL_EXCEPT; 219 220 return status_register & exceptions; 221#endif 222} 223 224int feraiseexcept(int exceptions) 225{ 226 fenv_t env; 227 fegetenv(&env); 228 229 exceptions &= FE_ALL_EXCEPT; 230 231#ifdef AK_ARCH_AARCH64 232 (void)exceptions; 233 TODO_AARCH64(); 234#else 235 // While the order in which the exceptions is raised is unspecified, FE_OVERFLOW and FE_UNDERFLOW must be raised before FE_INEXACT, so handle that case in this branch 236 if (exceptions & FE_INEXACT) { 237 env.__x87_fpu_env.__status_word &= ((u16)exceptions & ~FE_INEXACT); 238 fesetenv(&env); 239 asm volatile("fwait"); // "raise" the exception by performing a floating point operation 240 241 fegetenv(&env); 242 env.__x87_fpu_env.__status_word &= FE_INEXACT; 243 fesetenv(&env); 244 asm volatile("fwait"); 245 246 return 0; 247 } 248 249 env.__x87_fpu_env.__status_word &= exceptions; 250 fesetenv(&env); 251 asm volatile("fwait"); 252#endif 253 254 return 0; 255} 256}