fenv.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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 envinronment image in protected mode
  9. static_assert(sizeof(__x87_floating_point_environment) == 28);
  10. static u16 read_status_register()
  11. {
  12. u16 status_register;
  13. asm volatile("fstsw %0"
  14. : "=m"(status_register));
  15. return status_register;
  16. }
  17. static u16 read_control_word()
  18. {
  19. u16 control_word;
  20. asm volatile("fstcw %0"
  21. : "=m"(control_word));
  22. return control_word;
  23. }
  24. static void set_control_word(u16 new_control_word)
  25. {
  26. asm volatile("fldcw %0" ::"m"(new_control_word));
  27. }
  28. static u32 read_mxcsr()
  29. {
  30. u32 mxcsr;
  31. asm volatile("stmxcsr %0"
  32. : "=m"(mxcsr));
  33. return mxcsr;
  34. }
  35. static void set_mxcsr(u32 new_mxcsr)
  36. {
  37. asm volatile("ldmxcsr %0" ::"m"(new_mxcsr));
  38. }
  39. static constexpr u32 default_mxcsr_value = 0x1f80;
  40. extern "C" {
  41. int fegetenv(fenv_t* env)
  42. {
  43. if (!env)
  44. return 1;
  45. asm volatile("fstenv %0"
  46. : "=m"(env->__x87_fpu_env)::"memory");
  47. env->__mxcsr = read_mxcsr();
  48. return 0;
  49. }
  50. int fesetenv(const fenv_t* env)
  51. {
  52. if (!env)
  53. return 1;
  54. if (env == FE_DFL_ENV) {
  55. asm volatile("finit");
  56. set_mxcsr(default_mxcsr_value);
  57. return 0;
  58. }
  59. asm volatile("fldenv %0" ::"m"(env)
  60. : "memory");
  61. set_mxcsr(env->__mxcsr);
  62. return 0;
  63. }
  64. int feholdexcept(fenv_t* env)
  65. {
  66. fegetenv(env);
  67. fenv_t current_env;
  68. fegetenv(&current_env);
  69. current_env.__x87_fpu_env.__status_word &= ~FE_ALL_EXCEPT;
  70. current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit
  71. 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
  72. fesetenv(&current_env);
  73. return 0;
  74. }
  75. int feupdateenv(const fenv_t* env)
  76. {
  77. auto currently_raised_exceptions = fetestexcept(FE_ALL_EXCEPT);
  78. fesetenv(env);
  79. feraiseexcept(currently_raised_exceptions);
  80. return 0;
  81. }
  82. int fegetexceptflag(fexcept_t* except, int exceptions)
  83. {
  84. if (!except)
  85. return 1;
  86. *except = (uint16_t)fetestexcept(exceptions);
  87. return 0;
  88. }
  89. int fesetexceptflag(const fexcept_t* except, int exceptions)
  90. {
  91. if (!except)
  92. return 1;
  93. fenv_t current_env;
  94. fegetenv(&current_env);
  95. exceptions &= FE_ALL_EXCEPT;
  96. current_env.__x87_fpu_env.__status_word &= exceptions;
  97. current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Make sure exceptions don't get raised
  98. fesetenv(&current_env);
  99. return 0;
  100. }
  101. int fegetround()
  102. {
  103. // There's no way to signal whether the SSE rounding mode and x87 ones are different, so we assume they're the same
  104. return (read_status_register() >> 10) & 3;
  105. }
  106. int fesetround(int rounding_mode)
  107. {
  108. if (rounding_mode < FE_TONEAREST || rounding_mode > FE_TOWARDZERO)
  109. return 1;
  110. auto control_word = read_control_word();
  111. control_word &= ~(3 << 10);
  112. control_word |= rounding_mode << 10;
  113. set_control_word(control_word);
  114. auto mxcsr = read_mxcsr();
  115. mxcsr &= ~(3 << 13);
  116. mxcsr |= rounding_mode << 13;
  117. set_mxcsr(mxcsr);
  118. return 0;
  119. }
  120. int feclearexcept(int exceptions)
  121. {
  122. exceptions &= FE_ALL_EXCEPT;
  123. fenv_t current_env;
  124. fegetenv(&current_env);
  125. current_env.__x87_fpu_env.__status_word &= ~exceptions;
  126. current_env.__x87_fpu_env.__status_word &= ~(1 << 7); // Clear the "Exception Status Summary" bit
  127. fesetenv(&current_env);
  128. return 0;
  129. }
  130. int fetestexcept(int exceptions)
  131. {
  132. u16 status_register = read_status_register() & FE_ALL_EXCEPT;
  133. exceptions &= FE_ALL_EXCEPT;
  134. return status_register & exceptions;
  135. }
  136. int feraiseexcept(int exceptions)
  137. {
  138. fenv_t env;
  139. fegetenv(&env);
  140. exceptions &= FE_ALL_EXCEPT;
  141. // 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
  142. if (exceptions & FE_INEXACT) {
  143. env.__x87_fpu_env.__status_word &= ((u16)exceptions & ~FE_INEXACT);
  144. fesetenv(&env);
  145. asm volatile("fwait"); // "raise" the exception by performing a floating point operation
  146. fegetenv(&env);
  147. env.__x87_fpu_env.__status_word &= FE_INEXACT;
  148. fesetenv(&env);
  149. asm volatile("fwait");
  150. return 0;
  151. }
  152. env.__x87_fpu_env.__status_word &= exceptions;
  153. fesetenv(&env);
  154. asm volatile("fwait");
  155. return 0;
  156. }
  157. }