fenv.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * Copyright (c) 2021, Mițca Dumitru <dumitru0mitca@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Types.h>
  7. #include <fenv.h>
  8. // This is the size of the floating point environment image in protected mode
  9. static_assert(sizeof(__x87_floating_point_environment) == 28);
  10. #if !ARCH(AARCH64)
  11. static u16 read_status_register()
  12. {
  13. u16 status_register;
  14. asm volatile("fnstsw %0"
  15. : "=m"(status_register));
  16. return status_register;
  17. }
  18. static u16 read_control_word()
  19. {
  20. u16 control_word;
  21. asm volatile("fnstcw %0"
  22. : "=m"(control_word));
  23. return control_word;
  24. }
  25. static void set_control_word(u16 new_control_word)
  26. {
  27. asm volatile("fldcw %0" ::"m"(new_control_word));
  28. }
  29. static u32 read_mxcsr()
  30. {
  31. u32 mxcsr;
  32. asm volatile("stmxcsr %0"
  33. : "=m"(mxcsr));
  34. return mxcsr;
  35. }
  36. static void set_mxcsr(u32 new_mxcsr)
  37. {
  38. asm volatile("ldmxcsr %0" ::"m"(new_mxcsr));
  39. }
  40. static constexpr u32 default_mxcsr_value = 0x1f80;
  41. #endif
  42. extern "C" {
  43. int fegetenv(fenv_t* env)
  44. {
  45. if (!env)
  46. return 1;
  47. #if ARCH(AARCH64)
  48. (void)env;
  49. TODO_AARCH64();
  50. #else
  51. asm volatile("fnstenv %0"
  52. : "=m"(env->__x87_fpu_env)::"memory");
  53. env->__mxcsr = read_mxcsr();
  54. #endif
  55. return 0;
  56. }
  57. int fesetenv(fenv_t const* env)
  58. {
  59. if (!env)
  60. return 1;
  61. #if ARCH(AARCH64)
  62. (void)env;
  63. TODO_AARCH64();
  64. #else
  65. if (env == FE_DFL_ENV) {
  66. asm volatile("finit");
  67. set_mxcsr(default_mxcsr_value);
  68. return 0;
  69. }
  70. asm volatile("fldenv %0" ::"m"(env->__x87_fpu_env)
  71. : "memory");
  72. set_mxcsr(env->__mxcsr);
  73. #endif
  74. return 0;
  75. }
  76. int feholdexcept(fenv_t* env)
  77. {
  78. fegetenv(env);
  79. fenv_t current_env;
  80. fegetenv(&current_env);
  81. #if ARCH(AARCH64)
  82. (void)env;
  83. TODO_AARCH64();
  84. #else
  85. current_env.__x87_fpu_env.__status_word &= ~FE_ALL_EXCEPT;
  86. current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit
  87. 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
  88. #endif
  89. fesetenv(&current_env);
  90. return 0;
  91. }
  92. int feupdateenv(fenv_t const* env)
  93. {
  94. auto currently_raised_exceptions = fetestexcept(FE_ALL_EXCEPT);
  95. fesetenv(env);
  96. feraiseexcept(currently_raised_exceptions);
  97. return 0;
  98. }
  99. int fegetexceptflag(fexcept_t* except, int exceptions)
  100. {
  101. if (!except)
  102. return 1;
  103. *except = (uint16_t)fetestexcept(exceptions);
  104. return 0;
  105. }
  106. int fesetexceptflag(fexcept_t const* except, int exceptions)
  107. {
  108. if (!except)
  109. return 1;
  110. fenv_t current_env;
  111. fegetenv(&current_env);
  112. exceptions &= FE_ALL_EXCEPT;
  113. #if ARCH(AARCH64)
  114. (void)exceptions;
  115. (void)except;
  116. TODO_AARCH64();
  117. #else
  118. current_env.__x87_fpu_env.__status_word &= exceptions;
  119. current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Make sure exceptions don't get raised
  120. #endif
  121. fesetenv(&current_env);
  122. return 0;
  123. }
  124. int fegetround()
  125. {
  126. #if ARCH(AARCH64)
  127. TODO_AARCH64();
  128. #else
  129. // There's no way to signal whether the SSE rounding mode and x87 ones are different, so we assume they're the same
  130. return (read_status_register() >> 10) & 3;
  131. #endif
  132. }
  133. int fesetround(int rounding_mode)
  134. {
  135. if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOWARDZERO)
  136. return 1;
  137. #if ARCH(AARCH64)
  138. TODO_AARCH64();
  139. #else
  140. auto control_word = read_control_word();
  141. control_word &= ~(3 << 10);
  142. control_word |= rounding_mode << 10;
  143. set_control_word(control_word);
  144. auto mxcsr = read_mxcsr();
  145. mxcsr &= ~(3 << 13);
  146. mxcsr |= rounding_mode << 13;
  147. set_mxcsr(mxcsr);
  148. #endif
  149. return 0;
  150. }
  151. int feclearexcept(int exceptions)
  152. {
  153. exceptions &= FE_ALL_EXCEPT;
  154. fenv_t current_env;
  155. fegetenv(&current_env);
  156. #if ARCH(AARCH64)
  157. (void)exceptions;
  158. TODO_AARCH64();
  159. #else
  160. current_env.__x87_fpu_env.__status_word &= ~exceptions;
  161. current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit
  162. #endif
  163. fesetenv(&current_env);
  164. return 0;
  165. }
  166. int fetestexcept(int exceptions)
  167. {
  168. #if ARCH(AARCH64)
  169. (void)exceptions;
  170. TODO_AARCH64();
  171. #else
  172. u16 status_register = read_status_register() & FE_ALL_EXCEPT;
  173. exceptions &= FE_ALL_EXCEPT;
  174. return status_register & exceptions;
  175. #endif
  176. }
  177. int feraiseexcept(int exceptions)
  178. {
  179. fenv_t env;
  180. fegetenv(&env);
  181. exceptions &= FE_ALL_EXCEPT;
  182. #if ARCH(AARCH64)
  183. (void)exceptions;
  184. TODO_AARCH64();
  185. #else
  186. // 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
  187. if (exceptions & FE_INEXACT) {
  188. env.__x87_fpu_env.__status_word &= ((u16)exceptions & ~FE_INEXACT);
  189. fesetenv(&env);
  190. asm volatile("fwait"); // "raise" the exception by performing a floating point operation
  191. fegetenv(&env);
  192. env.__x87_fpu_env.__status_word &= FE_INEXACT;
  193. fesetenv(&env);
  194. asm volatile("fwait");
  195. return 0;
  196. }
  197. env.__x87_fpu_env.__status_word &= exceptions;
  198. fesetenv(&env);
  199. asm volatile("fwait");
  200. #endif
  201. return 0;
  202. }
  203. }