123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- /*
- * Copyright (c) 2021, Mițca Dumitru <dumitru0mitca@gmail.com>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/Types.h>
- #include <fenv.h>
- // This is the size of the floating point environment image in protected mode
- static_assert(sizeof(__x87_floating_point_environment) == 28);
- #if !ARCH(AARCH64)
- static u16 read_status_register()
- {
- u16 status_register;
- asm volatile("fnstsw %0"
- : "=m"(status_register));
- return status_register;
- }
- static u16 read_control_word()
- {
- u16 control_word;
- asm volatile("fnstcw %0"
- : "=m"(control_word));
- return control_word;
- }
- static void set_control_word(u16 new_control_word)
- {
- asm volatile("fldcw %0" ::"m"(new_control_word));
- }
- static u32 read_mxcsr()
- {
- u32 mxcsr;
- asm volatile("stmxcsr %0"
- : "=m"(mxcsr));
- return mxcsr;
- }
- static void set_mxcsr(u32 new_mxcsr)
- {
- asm volatile("ldmxcsr %0" ::"m"(new_mxcsr));
- }
- static constexpr u32 default_mxcsr_value = 0x1f80;
- #endif
- extern "C" {
- int fegetenv(fenv_t* env)
- {
- if (!env)
- return 1;
- #if ARCH(AARCH64)
- (void)env;
- TODO_AARCH64();
- #else
- asm volatile("fnstenv %0"
- : "=m"(env->__x87_fpu_env)::"memory");
- env->__mxcsr = read_mxcsr();
- #endif
- return 0;
- }
- int fesetenv(fenv_t const* env)
- {
- if (!env)
- return 1;
- #if ARCH(AARCH64)
- (void)env;
- TODO_AARCH64();
- #else
- if (env == FE_DFL_ENV) {
- asm volatile("finit");
- set_mxcsr(default_mxcsr_value);
- return 0;
- }
- asm volatile("fldenv %0" ::"m"(env->__x87_fpu_env)
- : "memory");
- set_mxcsr(env->__mxcsr);
- #endif
- return 0;
- }
- int feholdexcept(fenv_t* env)
- {
- fegetenv(env);
- fenv_t current_env;
- fegetenv(¤t_env);
- #if ARCH(AARCH64)
- (void)env;
- TODO_AARCH64();
- #else
- current_env.__x87_fpu_env.__status_word &= ~FE_ALL_EXCEPT;
- current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit
- 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
- #endif
- fesetenv(¤t_env);
- return 0;
- }
- int feupdateenv(fenv_t const* env)
- {
- auto currently_raised_exceptions = fetestexcept(FE_ALL_EXCEPT);
- fesetenv(env);
- feraiseexcept(currently_raised_exceptions);
- return 0;
- }
- int fegetexceptflag(fexcept_t* except, int exceptions)
- {
- if (!except)
- return 1;
- *except = (uint16_t)fetestexcept(exceptions);
- return 0;
- }
- int fesetexceptflag(fexcept_t const* except, int exceptions)
- {
- if (!except)
- return 1;
- fenv_t current_env;
- fegetenv(¤t_env);
- exceptions &= FE_ALL_EXCEPT;
- #if ARCH(AARCH64)
- (void)exceptions;
- (void)except;
- TODO_AARCH64();
- #else
- current_env.__x87_fpu_env.__status_word &= exceptions;
- current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Make sure exceptions don't get raised
- #endif
- fesetenv(¤t_env);
- return 0;
- }
- int fegetround()
- {
- #if ARCH(AARCH64)
- TODO_AARCH64();
- #else
- // There's no way to signal whether the SSE rounding mode and x87 ones are different, so we assume they're the same
- return (read_status_register() >> 10) & 3;
- #endif
- }
- int fesetround(int rounding_mode)
- {
- if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOWARDZERO)
- return 1;
- #if ARCH(AARCH64)
- TODO_AARCH64();
- #else
- auto control_word = read_control_word();
- control_word &= ~(3 << 10);
- control_word |= rounding_mode << 10;
- set_control_word(control_word);
- auto mxcsr = read_mxcsr();
- mxcsr &= ~(3 << 13);
- mxcsr |= rounding_mode << 13;
- set_mxcsr(mxcsr);
- #endif
- return 0;
- }
- int feclearexcept(int exceptions)
- {
- exceptions &= FE_ALL_EXCEPT;
- fenv_t current_env;
- fegetenv(¤t_env);
- #if ARCH(AARCH64)
- (void)exceptions;
- TODO_AARCH64();
- #else
- current_env.__x87_fpu_env.__status_word &= ~exceptions;
- current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit
- #endif
- fesetenv(¤t_env);
- return 0;
- }
- int fetestexcept(int exceptions)
- {
- #if ARCH(AARCH64)
- (void)exceptions;
- TODO_AARCH64();
- #else
- u16 status_register = read_status_register() & FE_ALL_EXCEPT;
- exceptions &= FE_ALL_EXCEPT;
- return status_register & exceptions;
- #endif
- }
- int feraiseexcept(int exceptions)
- {
- fenv_t env;
- fegetenv(&env);
- exceptions &= FE_ALL_EXCEPT;
- #if ARCH(AARCH64)
- (void)exceptions;
- TODO_AARCH64();
- #else
- // 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
- if (exceptions & FE_INEXACT) {
- env.__x87_fpu_env.__status_word &= ((u16)exceptions & ~FE_INEXACT);
- fesetenv(&env);
- asm volatile("fwait"); // "raise" the exception by performing a floating point operation
- fegetenv(&env);
- env.__x87_fpu_env.__status_word &= FE_INEXACT;
- fesetenv(&env);
- asm volatile("fwait");
- return 0;
- }
- env.__x87_fpu_env.__status_word &= exceptions;
- fesetenv(&env);
- asm volatile("fwait");
- #endif
- return 0;
- }
- }
|