Showing the changes to src/vm/math/Mathmod.cpp (1 of 3 files). Show all
Unified · Split
| @@ -4,6 +4,16 @@ · 3 unmodified lines | |||
| 4 | // Author: arr | 4 | // Author: arr |
| 5 | // | 5 | // |
| 6 | 6 | ||
| 7 | #ifdef _MSC_VER | ||
| 8 | // Pragma for floating point support - https://msdn.microsoft.com/en-us/library/bfwa91s0.aspx | ||
| 9 | #pragma fenv_access (on) | ||
| 10 | #else | ||
| 11 | // Pragma for floating point support - http://en.cppreference.com/w/cpp/numeric/fenv | ||
| 12 | #pragma STDC FENV_ACCESS ON | ||
| 13 | #endif | ||
| 14 | |||
| 15 | #include <cfenv> | ||
| 16 | |||
| 7 | #include <debug.h> | 17 | #include <debug.h> |
| 8 | #include "Mathmod.h" | 18 | #include "Mathmod.h" |
| 9 | 19 | ||
| @@ -96,30 +106,160 @@ namespace · 86 unmodified lines | |||
| 96 | }; | 106 | }; |
| 97 | 107 | ||
| 98 | const word_t Mathmodlen = 68; | 108 | const word_t Mathmodlen = 68; |
| 109 | |||
| 110 | // Convert from std floating point round to limbo | ||
| 111 | word_t convert_to_limbo_fp_round(int r) | ||
| 112 | { | ||
| 113 | switch (r & FE_ROUND_MASK) | ||
| 114 | { | ||
| 115 | default: | ||
| 116 | assert(false && "Invalid floating point rounding flag"); | ||
| 117 | case FE_TONEAREST: | ||
| 118 | return Math_RND_NR; | ||
| 119 | case FE_DOWNWARD: | ||
| 120 | return Math_RND_NINF; | ||
| 121 | case FE_UPWARD: | ||
| 122 | return Math_RND_PINF; | ||
| 123 | case FE_TOWARDZERO: | ||
| 124 | return Math_RND_Z; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | // Convert from std floating point exception to limbo | ||
| 129 | word_t convert_to_limbo_fp_exception(std::fexcept_t e) | ||
| 130 | { | ||
| 131 | word_t e_flags{}; | ||
| 132 | if (e & FE_INEXACT) e_flags |= Math_INEX; | ||
| 133 | if (e & FE_UNDERFLOW) e_flags |= Math_UNFL; | ||
| 134 | if (e & FE_OVERFLOW) e_flags |= Math_OVFL; | ||
| 135 | if (e & FE_DIVBYZERO) e_flags |= Math_ZDIV; | ||
| 136 | if (e & FE_INVALID) e_flags |= Math_INVAL; | ||
| 137 | |||
| 138 | return e_flags; | ||
| 139 | } | ||
| 140 | |||
| 141 | // Convert from limbo floating point round to std | ||
| 142 | int convert_to_std_fp_round(word_t r) | ||
| 143 | { | ||
| 144 | switch (r & Math_RND_MASK) | ||
| 145 | { | ||
| 146 | default: | ||
| 147 | assert(false && "Invalid floating point rounding flag"); | ||
| 148 | case Math_RND_NR: | ||
| 149 | return FE_TONEAREST; | ||
| 150 | case Math_RND_NINF: | ||
| 151 | return FE_DOWNWARD; | ||
| 152 | case Math_RND_PINF: | ||
| 153 | return FE_UPWARD; | ||
| 154 | case Math_RND_Z: | ||
| 155 | return FE_TOWARDZERO; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | // Convert from limbo floating point exception to std | ||
| 160 | std::fexcept_t convert_to_std_fp_exception(word_t e) | ||
| 161 | { | ||
| 162 | std::fexcept_t e_flags{}; | ||
| 163 | if (e & Math_INEX) e_flags |= FE_INEXACT; | ||
| 164 | if (e & Math_UNFL) e_flags |= FE_UNDERFLOW; | ||
| 165 | if (e & Math_OVFL) e_flags |= FE_OVERFLOW; | ||
| 166 | if (e & Math_ZDIV) e_flags |= FE_DIVBYZERO; | ||
| 167 | if (e & Math_INVAL) e_flags |= FE_INVALID; | ||
| 168 | |||
| 169 | return e_flags; | ||
| 170 | } | ||
| 171 | |||
| 172 | // Get floating point exception flags | ||
| 173 | word_t get_fp_exception_flags() | ||
| 174 | { | ||
| 175 | std::fexcept_t e_flags; | ||
| 176 | int ec = std::fegetexceptflag(&e_flags, FE_ALL_EXCEPT); | ||
| 177 | if (ec != 0) | ||
| 178 | { | ||
| 179 | assert(false && "Failed to get except flag"); | ||
| 180 | return 0; | ||
| 181 | } | ||
| 182 | |||
| 183 | return convert_to_limbo_fp_exception(e_flags); | ||
| 184 | } | ||
| 185 | |||
| 186 | // Get all floating point flags | ||
| 187 | word_t get_all_fp_flags() | ||
| 188 | { | ||
| 189 | const auto rnd_flag = std::fegetround(); | ||
| 190 | const auto e_flags = get_fp_exception_flags(); | ||
| 191 | return (convert_to_limbo_fp_round(rnd_flag) | e_flags); | ||
| 192 | } | ||
| 193 | |||
| 194 | // Set floating point control flags | ||
| 195 | void set_fp_control_flags(std::fexcept_t e_flags, std::fexcept_t e_mask, int rnd_flag, int rnd_mask) | ||
| 196 | { | ||
| 197 | int ec; | ||
| 198 | |||
| 199 | // [SPEC] No current mechanism to set floating point exception control flags | ||
| 200 | |||
| 201 | if (rnd_mask != 0) | ||
| 202 | { | ||
| 203 | ec = std::fesetround(rnd_flag & rnd_mask); | ||
| 204 | assert(ec == 0); | ||
| 205 | (void)ec; | ||
| 206 | } | ||
| 207 | } | ||
| 99 | } | 208 | } |
| 100 | 209 | ||
| 101 | void | 210 | void |
| 102 | Mathmodinit(void) | 211 | Mathmodinit(void) |
| 103 | { | 212 | { |
| 104 | disvm::runtime::builtin::register_module_exports(Math_PATH, Mathmodlen, Mathmodtab); | 213 | disvm::runtime::builtin::register_module_exports(Math_PATH, Mathmodlen, Mathmodtab); |
| 214 | |||
| 215 | // Inferno floating point flags (http://www.vitanuova.com/inferno/man/2/math-fp.html) | ||
| 216 | set_fp_control_flags( | ||
| 217 | FE_INEXACT, // Inexact is fatal | ||
| 218 | FE_ALL_EXCEPT, | ||
| 219 | FE_TONEAREST, // Round to nearest even | ||
| 220 | FE_ROUND_MASK); | ||
| 105 | } | 221 | } |
| 106 | 222 | ||
| 107 | void | 223 | void |
| 108 | Math_FPcontrol(vm_registers_t &r, vm_t &vm) | 224 | Math_FPcontrol(vm_registers_t &r, vm_t &) |
| 109 | { | 225 | { |
| 110 | auto &fp = r.stack.peek_frame()->base<F_Math_FPcontrol>(); | 226 | auto &fp = r.stack.peek_frame()->base<F_Math_FPcontrol>(); |
| 111 | 227 | ||
| 112 | // [PAL] Properly implement floating point control | 228 | const auto prev_fp_flags = get_all_fp_flags(); |
| 113 | disvm::debug::log_msg(component_trace_t::builtin, log_level_t::warning, "FPControl is not properly implemented"); | 229 | |
| 230 | const auto e_flags = convert_to_std_fp_exception(fp.r & fp.mask); | ||
| 231 | const auto rnd_flag = convert_to_std_fp_round(fp.r & fp.mask); | ||
| 114 | 232 | ||
| 115 | *fp.ret = fp.mask; | 233 | set_fp_control_flags( |
| 234 | e_flags, | ||
| 235 | fp.mask & FE_ALL_EXCEPT, | ||
| 236 | rnd_flag, | ||
| 237 | fp.mask & FE_ROUND_MASK); | ||
| 238 | |||
| 239 | *fp.ret = (prev_fp_flags & fp.mask); | ||
| 116 | } | 240 | } |
| 117 | 241 | ||
| 118 | void | 242 | void |
| 119 | Math_FPstatus(vm_registers_t &r, vm_t &vm) | 243 | Math_FPstatus(vm_registers_t &r, vm_t &) |
| 120 | { | 244 | { |
| 121 | auto &fp = r.stack.peek_frame()->base<F_Math_FPstatus>(); | 245 | auto &fp = r.stack.peek_frame()->base<F_Math_FPstatus>(); |
| 122 | throw vm_system_exception{ "Function not implemented" }; | 246 | |
| 247 | const auto prev_e_flags = get_fp_exception_flags(); | ||
| 248 | |||
| 249 | // Clear all exceptions | ||
| 250 | int ec = std::feclearexcept(FE_ALL_EXCEPT); | ||
| 251 | assert(ec == 0); | ||
| 252 | (void)ec; | ||
| 253 | |||
| 254 | const auto e_flags = convert_to_std_fp_exception(fp.r); | ||
| 255 | const auto e_mask = convert_to_std_fp_exception(fp.mask); | ||
| 256 | |||
| 257 | // Set exception flags | ||
| 258 | ec = std::fesetexceptflag(&e_flags, static_cast<const int>(e_mask)); | ||
| 259 | assert(ec == 0); | ||
| 260 | (void)ec; | ||
| 261 | |||
| 262 | *fp.ret = (prev_e_flags & e_mask); | ||
| 123 | } | 263 | } |
| 124 | 264 | ||
| 125 | void | 265 | void |
| @@ -392,17 +532,17 @@ Math_gemm(vm_registers_t &r, vm_t &vm) · 266 unmodified lines | |||
| 392 | } | 532 | } |
| 393 | 533 | ||
| 394 | void | 534 | void |
| 395 | Math_getFPcontrol(vm_registers_t &r, vm_t &vm) | 535 | Math_getFPcontrol(vm_registers_t &r, vm_t &) |
| 396 | { | 536 | { |
| 397 | auto &fp = r.stack.peek_frame()->base<F_Math_getFPcontrol>(); | 537 | auto &fp = r.stack.peek_frame()->base<F_Math_getFPcontrol>(); |
| 398 | throw vm_system_exception{ "Function not implemented" }; | 538 | *fp.ret = get_all_fp_flags(); |
| 399 | } | 539 | } |
| 400 | 540 | ||
| 401 | void | 541 | void |
| 402 | Math_getFPstatus(vm_registers_t &r, vm_t &vm) | 542 | Math_getFPstatus(vm_registers_t &r, vm_t &) |
| 403 | { | 543 | { |
| 404 | auto &fp = r.stack.peek_frame()->base<F_Math_getFPstatus>(); | 544 | auto &fp = r.stack.peek_frame()->base<F_Math_getFPstatus>(); |
| 405 | throw vm_system_exception{ "Function not implemented" }; | 545 | *fp.ret = get_fp_exception_flags(); |
| 406 | } | 546 | } |
| 407 | 547 | ||
| 408 | void | 548 | void |