History

Add support for floating point flags - Setting exception flag traps is not properly supported - no C++ support

arr committed 9 years ago

Commit b3354b21 parent b1b22c23 files · +203 −13Browse files

Unified · Split

3 files
  1. limbo/basic_limbo_tests.b +52 −2
  2. src/include/vm_memory.h +1 −1
  3. src/vm/math/Mathmod.cpp +150 −10
limbo/basic_limbo_tests.b+52−2
@@ -4,6 +4,9 @@ include "sys.m"; · 3 unmodified lines
4include "Exec.m";4include "Exec.m";
5include "external_module.m";5include "external_module.m";
6 6
7include "math.m";
8
9m : Math;
7sys : Sys;10sys : Sys;
8 11
9TEST_FAILED : exception;12TEST_FAILED : exception;
@@ -12,6 +15,8 @@ init(args: list of string) · 2 unmodified lines
12{15{
13 a := "foobar!";16 a := "foobar!";
14 sys = load Sys Sys->PATH;17 sys = load Sys Sys->PATH;
18 m = load Math Math->PATH;
19
15 big_val := big 16r12345;20 big_val := big 16r12345;
16 sys->print("Hello, %s %o %d %x %bd %f\n%s%%%% %H\n\n", "world!", 8, 10, 16, big_val, 0.1, "------------------\n", a);21 sys->print("Hello, %s %o %d %x %bd %f\n%s%%%% %H\n\n", "world!", 8, 10, 16, big_val, 0.1, "------------------\n", a);
17 22
@@ -22,6 +27,7 @@ init(args: list of string) · 4 unmodified lines
22 args = tl args;27 args = tl args;
23 }28 }
24 29
30 test_floating_point();
25 test_poly();31 test_poly();
26 test_function_refs();32 test_function_refs();
27 test_alt();33 test_alt();
@@ -40,6 +46,50 @@ init(args: list of string) · 12 unmodified lines
40 test_for_loop_function_call();46 test_for_loop_function_call();
41}47}
42 48
49test_floating_point()
50{
51 sys->print("test_floating_point()\n");
52
53 all_flags : con (Math->INVAL | Math->ZDIV | Math->OVFL | Math->UNFL | Math->INEX) | Math->RND_MASK;
54
55 R : real;
56 m->FPcontrol(Math->RND_NR, all_flags);
57 R = m->rint(12.1);
58 if (R >= 12.1) raise TEST_FAILED();
59
60 m->FPcontrol(Math->RND_NINF, all_flags);
61 R = m->rint(13.1);
62 if (R >= 13.1) raise TEST_FAILED();
63
64 R = m->rint(13.9);
65 if (R >= 13.9) raise TEST_FAILED();
66
67 m->FPcontrol(Math->RND_PINF, all_flags);
68 R = m->rint(14.1);
69 if (R <= 14.1) raise TEST_FAILED();
70
71 R = m->rint(14.9);
72 if (R <= 14.9) raise TEST_FAILED();
73
74 m->FPcontrol(Math->RND_Z, all_flags);
75 R = m->rint(-14.1);
76 if (R <= -14.1) raise TEST_FAILED();
77
78 R = m->rint(-14.9);
79 if (R <= -14.9) raise TEST_FAILED();
80
81 # Reset exception flags
82 m->FPstatus(0, all_flags);
83 status := m->getFPstatus();
84 if (status & Math->ZDIV) raise TEST_FAILED();
85 if (status != 0) raise TEST_FAILED();
86
87 R = 12. / real status;
88
89 status = m->getFPstatus();
90 if (!(status & Math->ZDIV)) raise TEST_FAILED();
91}
92
43reverse[T](l: list of T): list of T93reverse[T](l: list of T): list of T
44{94{
45 rl: list of T;95 rl: list of T;
@@ -829,9 +879,9 @@ test_string() · 783 unmodified lines
829 w_val := int s_w;879 w_val := int s_w;
830 if (w_val != 12345) raise TEST_FAILED();880 if (w_val != 12345) raise TEST_FAILED();
831 881
832 s_r := "3.14";882 s_r := "3.00";
833 r_val := real s_r;883 r_val := real s_r;
834 if (r_val != 3.14) raise TEST_FAILED();884 if (int m->rint(r_val * 100.) != int m->rint(3.00 * 100.)) raise TEST_FAILED();
835 885
836 s_b := "987532";886 s_b := "987532";
837 b_val := big s_b;887 b_val := big s_b;
src/include/vm_memory.h+1−1
@@ -57,7 +57,7 @@ namespace disvm · 56 unmodified lines
57 template<typename CB>57 template<typename CB>
58 void enum_pointer_fields(const type_descriptor_t &type_desc, void *data, CB callback)58 void enum_pointer_fields(const type_descriptor_t &type_desc, void *data, CB callback)
59 {59 {
60 assert(data != nullptr && callback != nullptr);60 assert(data != nullptr);
61 61
62 auto memory = reinterpret_cast<word_t *>(data);62 auto memory = reinterpret_cast<word_t *>(data);
63 for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8)63 for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8)
src/vm/math/Mathmod.cpp+150−10
@@ -4,6 +4,16 @@ · 3 unmodified lines
4// Author: arr4// 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
101void210void
102Mathmodinit(void)211Mathmodinit(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
107void223void
108Math_FPcontrol(vm_registers_t &r, vm_t &vm)224Math_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 control228 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
118void242void
119Math_FPstatus(vm_registers_t &r, vm_t &vm)243Math_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
125void265void
@@ -392,17 +532,17 @@ Math_gemm(vm_registers_t &r, vm_t &vm) · 266 unmodified lines
392}532}
393 533
394void534void
395Math_getFPcontrol(vm_registers_t &r, vm_t &vm)535Math_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
401void541void
402Math_getFPstatus(vm_registers_t &r, vm_t &vm)542Math_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
408void548void