| @@ -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 |