1 /*
2 * Helpers for floating point instructions.
3 *
4 * Copyright (c) 2007 Jocelyn Mayer
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "qemu/osdep.h"
21 #include "cpu.h"
22 #include "exec/exec-all.h"
23 #include "exec/helper-proto.h"
24 #include "fpu/softfloat.h"
25
26 #define FP_STATUS (env->fp_status)
27
28
helper_setroundmode(CPUAlphaState * env,uint32_t val)29 void helper_setroundmode(CPUAlphaState *env, uint32_t val)
30 {
31 set_float_rounding_mode(val, &FP_STATUS);
32 }
33
helper_setflushzero(CPUAlphaState * env,uint32_t val)34 void helper_setflushzero(CPUAlphaState *env, uint32_t val)
35 {
36 set_flush_to_zero(val, &FP_STATUS);
37 }
38
39 #define CONVERT_BIT(X, SRC, DST) \
40 (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
41
soft_to_fpcr_exc(CPUAlphaState * env)42 static uint32_t soft_to_fpcr_exc(CPUAlphaState *env)
43 {
44 uint8_t exc = get_float_exception_flags(&FP_STATUS);
45 uint32_t ret = 0;
46
47 if (unlikely(exc)) {
48 set_float_exception_flags(0, &FP_STATUS);
49 ret |= CONVERT_BIT(exc, float_flag_invalid, FPCR_INV);
50 ret |= CONVERT_BIT(exc, float_flag_divbyzero, FPCR_DZE);
51 ret |= CONVERT_BIT(exc, float_flag_overflow, FPCR_OVF);
52 ret |= CONVERT_BIT(exc, float_flag_underflow, FPCR_UNF);
53 ret |= CONVERT_BIT(exc, float_flag_inexact, FPCR_INE);
54 }
55
56 return ret;
57 }
58
fp_exc_raise1(CPUAlphaState * env,uintptr_t retaddr,uint32_t exc,uint32_t regno,uint32_t hw_exc)59 static void fp_exc_raise1(CPUAlphaState *env, uintptr_t retaddr,
60 uint32_t exc, uint32_t regno, uint32_t hw_exc)
61 {
62 hw_exc |= CONVERT_BIT(exc, FPCR_INV, EXC_M_INV);
63 hw_exc |= CONVERT_BIT(exc, FPCR_DZE, EXC_M_DZE);
64 hw_exc |= CONVERT_BIT(exc, FPCR_OVF, EXC_M_FOV);
65 hw_exc |= CONVERT_BIT(exc, FPCR_UNF, EXC_M_UNF);
66 hw_exc |= CONVERT_BIT(exc, FPCR_INE, EXC_M_INE);
67 hw_exc |= CONVERT_BIT(exc, FPCR_IOV, EXC_M_IOV);
68
69 arith_excp(env, retaddr, hw_exc, 1ull << regno);
70 }
71
72 /* Raise exceptions for ieee fp insns without software completion.
73 In that case there are no exceptions that don't trap; the mask
74 doesn't apply. */
helper_fp_exc_raise(CPUAlphaState * env,uint32_t ignore,uint32_t regno)75 void helper_fp_exc_raise(CPUAlphaState *env, uint32_t ignore, uint32_t regno)
76 {
77 uint32_t exc = env->error_code;
78 if (exc) {
79 env->fpcr |= exc;
80 exc &= ~ignore;
81 if (exc) {
82 fp_exc_raise1(env, GETPC(), exc, regno, 0);
83 }
84 }
85 }
86
87 /* Raise exceptions for ieee fp insns with software completion. */
helper_fp_exc_raise_s(CPUAlphaState * env,uint32_t ignore,uint32_t regno)88 void helper_fp_exc_raise_s(CPUAlphaState *env, uint32_t ignore, uint32_t regno)
89 {
90 uint32_t exc = env->error_code & ~ignore;
91 if (exc) {
92 env->fpcr |= exc;
93 exc &= env->fpcr_exc_enable;
94 /*
95 * In system mode, the software handler gets invoked
96 * for any non-ignored exception.
97 * In user mode, the kernel's software handler only
98 * delivers a signal if the exception is enabled.
99 */
100 #ifdef CONFIG_USER_ONLY
101 if (!exc) {
102 return;
103 }
104 #endif
105 fp_exc_raise1(env, GETPC(), exc, regno, EXC_M_SWC);
106 }
107 }
108
109 /* Input handing without software completion. Trap for all
110 non-finite numbers. */
helper_ieee_input(CPUAlphaState * env,uint64_t val)111 void helper_ieee_input(CPUAlphaState *env, uint64_t val)
112 {
113 uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
114 uint64_t frac = val & 0xfffffffffffffull;
115
116 if (exp == 0) {
117 /* Denormals without /S raise an exception. */
118 if (frac != 0) {
119 arith_excp(env, GETPC(), EXC_M_INV, 0);
120 }
121 } else if (exp == 0x7ff) {
122 /* Infinity or NaN. */
123 env->fpcr |= FPCR_INV;
124 arith_excp(env, GETPC(), EXC_M_INV, 0);
125 }
126 }
127
128 /* Similar, but does not trap for infinities. Used for comparisons. */
helper_ieee_input_cmp(CPUAlphaState * env,uint64_t val)129 void helper_ieee_input_cmp(CPUAlphaState *env, uint64_t val)
130 {
131 uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
132 uint64_t frac = val & 0xfffffffffffffull;
133
134 if (exp == 0) {
135 /* Denormals without /S raise an exception. */
136 if (frac != 0) {
137 arith_excp(env, GETPC(), EXC_M_INV, 0);
138 }
139 } else if (exp == 0x7ff && frac) {
140 /* NaN. */
141 env->fpcr |= FPCR_INV;
142 arith_excp(env, GETPC(), EXC_M_INV, 0);
143 }
144 }
145
146 /* Input handing with software completion. Trap for denorms, unless DNZ
147 is set. If we try to support DNOD (which none of the produced hardware
148 did, AFAICS), we'll need to suppress the trap when FPCR.DNOD is set;
149 then the code downstream of that will need to cope with denorms sans
150 flush_input_to_zero. Most of it should work sanely, but there's
151 nothing to compare with. */
helper_ieee_input_s(CPUAlphaState * env,uint64_t val)152 void helper_ieee_input_s(CPUAlphaState *env, uint64_t val)
153 {
154 if (unlikely(2 * val - 1 < 0x1fffffffffffffull)
155 && !env->fp_status.flush_inputs_to_zero) {
156 arith_excp(env, GETPC(), EXC_M_INV | EXC_M_SWC, 0);
157 }
158 }
159
160 /* S floating (single) */
161
162 /* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg. */
float32_to_s_int(uint32_t fi)163 static inline uint64_t float32_to_s_int(uint32_t fi)
164 {
165 uint32_t frac = fi & 0x7fffff;
166 uint32_t sign = fi >> 31;
167 uint32_t exp_msb = (fi >> 30) & 1;
168 uint32_t exp_low = (fi >> 23) & 0x7f;
169 uint32_t exp;
170
171 exp = (exp_msb << 10) | exp_low;
172 if (exp_msb) {
173 if (exp_low == 0x7f) {
174 exp = 0x7ff;
175 }
176 } else {
177 if (exp_low != 0x00) {
178 exp |= 0x380;
179 }
180 }
181
182 return (((uint64_t)sign << 63)
183 | ((uint64_t)exp << 52)
184 | ((uint64_t)frac << 29));
185 }
186
float32_to_s(float32 fa)187 static inline uint64_t float32_to_s(float32 fa)
188 {
189 CPU_FloatU a;
190 a.f = fa;
191 return float32_to_s_int(a.l);
192 }
193
s_to_float32_int(uint64_t a)194 static inline uint32_t s_to_float32_int(uint64_t a)
195 {
196 return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff);
197 }
198
s_to_float32(uint64_t a)199 static inline float32 s_to_float32(uint64_t a)
200 {
201 CPU_FloatU r;
202 r.l = s_to_float32_int(a);
203 return r.f;
204 }
205
helper_s_to_memory(uint64_t a)206 uint32_t helper_s_to_memory(uint64_t a)
207 {
208 return s_to_float32_int(a);
209 }
210
helper_memory_to_s(uint32_t a)211 uint64_t helper_memory_to_s(uint32_t a)
212 {
213 return float32_to_s_int(a);
214 }
215
helper_adds(CPUAlphaState * env,uint64_t a,uint64_t b)216 uint64_t helper_adds(CPUAlphaState *env, uint64_t a, uint64_t b)
217 {
218 float32 fa, fb, fr;
219
220 fa = s_to_float32(a);
221 fb = s_to_float32(b);
222 fr = float32_add(fa, fb, &FP_STATUS);
223 env->error_code = soft_to_fpcr_exc(env);
224
225 return float32_to_s(fr);
226 }
227
helper_subs(CPUAlphaState * env,uint64_t a,uint64_t b)228 uint64_t helper_subs(CPUAlphaState *env, uint64_t a, uint64_t b)
229 {
230 float32 fa, fb, fr;
231
232 fa = s_to_float32(a);
233 fb = s_to_float32(b);
234 fr = float32_sub(fa, fb, &FP_STATUS);
235 env->error_code = soft_to_fpcr_exc(env);
236
237 return float32_to_s(fr);
238 }
239
helper_muls(CPUAlphaState * env,uint64_t a,uint64_t b)240 uint64_t helper_muls(CPUAlphaState *env, uint64_t a, uint64_t b)
241 {
242 float32 fa, fb, fr;
243
244 fa = s_to_float32(a);
245 fb = s_to_float32(b);
246 fr = float32_mul(fa, fb, &FP_STATUS);
247 env->error_code = soft_to_fpcr_exc(env);
248
249 return float32_to_s(fr);
250 }
251
helper_divs(CPUAlphaState * env,uint64_t a,uint64_t b)252 uint64_t helper_divs(CPUAlphaState *env, uint64_t a, uint64_t b)
253 {
254 float32 fa, fb, fr;
255
256 fa = s_to_float32(a);
257 fb = s_to_float32(b);
258 fr = float32_div(fa, fb, &FP_STATUS);
259 env->error_code = soft_to_fpcr_exc(env);
260
261 return float32_to_s(fr);
262 }
263
helper_sqrts(CPUAlphaState * env,uint64_t a)264 uint64_t helper_sqrts(CPUAlphaState *env, uint64_t a)
265 {
266 float32 fa, fr;
267
268 fa = s_to_float32(a);
269 fr = float32_sqrt(fa, &FP_STATUS);
270 env->error_code = soft_to_fpcr_exc(env);
271
272 return float32_to_s(fr);
273 }
274
275
276 /* T floating (double) */
t_to_float64(uint64_t a)277 static inline float64 t_to_float64(uint64_t a)
278 {
279 /* Memory format is the same as float64 */
280 CPU_DoubleU r;
281 r.ll = a;
282 return r.d;
283 }
284
float64_to_t(float64 fa)285 static inline uint64_t float64_to_t(float64 fa)
286 {
287 /* Memory format is the same as float64 */
288 CPU_DoubleU r;
289 r.d = fa;
290 return r.ll;
291 }
292
helper_addt(CPUAlphaState * env,uint64_t a,uint64_t b)293 uint64_t helper_addt(CPUAlphaState *env, uint64_t a, uint64_t b)
294 {
295 float64 fa, fb, fr;
296
297 fa = t_to_float64(a);
298 fb = t_to_float64(b);
299 fr = float64_add(fa, fb, &FP_STATUS);
300 env->error_code = soft_to_fpcr_exc(env);
301
302 return float64_to_t(fr);
303 }
304
helper_subt(CPUAlphaState * env,uint64_t a,uint64_t b)305 uint64_t helper_subt(CPUAlphaState *env, uint64_t a, uint64_t b)
306 {
307 float64 fa, fb, fr;
308
309 fa = t_to_float64(a);
310 fb = t_to_float64(b);
311 fr = float64_sub(fa, fb, &FP_STATUS);
312 env->error_code = soft_to_fpcr_exc(env);
313
314 return float64_to_t(fr);
315 }
316
helper_mult(CPUAlphaState * env,uint64_t a,uint64_t b)317 uint64_t helper_mult(CPUAlphaState *env, uint64_t a, uint64_t b)
318 {
319 float64 fa, fb, fr;
320
321 fa = t_to_float64(a);
322 fb = t_to_float64(b);
323 fr = float64_mul(fa, fb, &FP_STATUS);
324 env->error_code = soft_to_fpcr_exc(env);
325
326 return float64_to_t(fr);
327 }
328
helper_divt(CPUAlphaState * env,uint64_t a,uint64_t b)329 uint64_t helper_divt(CPUAlphaState *env, uint64_t a, uint64_t b)
330 {
331 float64 fa, fb, fr;
332
333 fa = t_to_float64(a);
334 fb = t_to_float64(b);
335 fr = float64_div(fa, fb, &FP_STATUS);
336 env->error_code = soft_to_fpcr_exc(env);
337
338 return float64_to_t(fr);
339 }
340
helper_sqrtt(CPUAlphaState * env,uint64_t a)341 uint64_t helper_sqrtt(CPUAlphaState *env, uint64_t a)
342 {
343 float64 fa, fr;
344
345 fa = t_to_float64(a);
346 fr = float64_sqrt(fa, &FP_STATUS);
347 env->error_code = soft_to_fpcr_exc(env);
348
349 return float64_to_t(fr);
350 }
351
352 /* Comparisons */
helper_cmptun(CPUAlphaState * env,uint64_t a,uint64_t b)353 uint64_t helper_cmptun(CPUAlphaState *env, uint64_t a, uint64_t b)
354 {
355 float64 fa, fb;
356 uint64_t ret = 0;
357
358 fa = t_to_float64(a);
359 fb = t_to_float64(b);
360
361 if (float64_unordered_quiet(fa, fb, &FP_STATUS)) {
362 ret = 0x4000000000000000ULL;
363 }
364 env->error_code = soft_to_fpcr_exc(env);
365
366 return ret;
367 }
368
helper_cmpteq(CPUAlphaState * env,uint64_t a,uint64_t b)369 uint64_t helper_cmpteq(CPUAlphaState *env, uint64_t a, uint64_t b)
370 {
371 float64 fa, fb;
372 uint64_t ret = 0;
373
374 fa = t_to_float64(a);
375 fb = t_to_float64(b);
376
377 if (float64_eq_quiet(fa, fb, &FP_STATUS)) {
378 ret = 0x4000000000000000ULL;
379 }
380 env->error_code = soft_to_fpcr_exc(env);
381
382 return ret;
383 }
384
helper_cmptle(CPUAlphaState * env,uint64_t a,uint64_t b)385 uint64_t helper_cmptle(CPUAlphaState *env, uint64_t a, uint64_t b)
386 {
387 float64 fa, fb;
388 uint64_t ret = 0;
389
390 fa = t_to_float64(a);
391 fb = t_to_float64(b);
392
393 if (float64_le(fa, fb, &FP_STATUS)) {
394 ret = 0x4000000000000000ULL;
395 }
396 env->error_code = soft_to_fpcr_exc(env);
397
398 return ret;
399 }
400
helper_cmptlt(CPUAlphaState * env,uint64_t a,uint64_t b)401 uint64_t helper_cmptlt(CPUAlphaState *env, uint64_t a, uint64_t b)
402 {
403 float64 fa, fb;
404 uint64_t ret = 0;
405
406 fa = t_to_float64(a);
407 fb = t_to_float64(b);
408
409 if (float64_lt(fa, fb, &FP_STATUS)) {
410 ret = 0x4000000000000000ULL;
411 }
412 env->error_code = soft_to_fpcr_exc(env);
413
414 return ret;
415 }
416
417 /* Floating point format conversion */
helper_cvtts(CPUAlphaState * env,uint64_t a)418 uint64_t helper_cvtts(CPUAlphaState *env, uint64_t a)
419 {
420 float64 fa;
421 float32 fr;
422
423 fa = t_to_float64(a);
424 fr = float64_to_float32(fa, &FP_STATUS);
425 env->error_code = soft_to_fpcr_exc(env);
426
427 return float32_to_s(fr);
428 }
429
helper_cvtst(CPUAlphaState * env,uint64_t a)430 uint64_t helper_cvtst(CPUAlphaState *env, uint64_t a)
431 {
432 float32 fa;
433 float64 fr;
434
435 fa = s_to_float32(a);
436 fr = float32_to_float64(fa, &FP_STATUS);
437 env->error_code = soft_to_fpcr_exc(env);
438
439 return float64_to_t(fr);
440 }
441
helper_cvtqs(CPUAlphaState * env,uint64_t a)442 uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a)
443 {
444 float32 fr = int64_to_float32(a, &FP_STATUS);
445 env->error_code = soft_to_fpcr_exc(env);
446
447 return float32_to_s(fr);
448 }
449
450 /* Implement float64 to uint64_t conversion without saturation -- we must
451 supply the truncated result. This behaviour is used by the compiler
452 to get unsigned conversion for free with the same instruction. */
453
do_cvttq(CPUAlphaState * env,uint64_t a,int roundmode)454 static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode)
455 {
456 float64 fa;
457 int64_t ret;
458 uint32_t exc;
459
460 fa = t_to_float64(a);
461 ret = float64_to_int64_modulo(fa, roundmode, &FP_STATUS);
462
463 exc = get_float_exception_flags(&FP_STATUS);
464 if (unlikely(exc)) {
465 set_float_exception_flags(0, &FP_STATUS);
466
467 /* We need to massage the resulting exceptions. */
468 if (exc & float_flag_invalid_cvti) {
469 /* Overflow, either normal or infinity. */
470 if (float64_is_infinity(fa)) {
471 exc = FPCR_INV;
472 } else {
473 exc = FPCR_IOV | FPCR_INE;
474 }
475 } else if (exc & float_flag_invalid) {
476 exc = FPCR_INV;
477 } else if (exc & float_flag_inexact) {
478 exc = FPCR_INE;
479 }
480 }
481 env->error_code = exc;
482
483 return ret;
484 }
485
helper_cvttq(CPUAlphaState * env,uint64_t a)486 uint64_t helper_cvttq(CPUAlphaState *env, uint64_t a)
487 {
488 return do_cvttq(env, a, FP_STATUS.float_rounding_mode);
489 }
490
helper_cvttq_c(CPUAlphaState * env,uint64_t a)491 uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a)
492 {
493 return do_cvttq(env, a, float_round_to_zero);
494 }
495
helper_cvtqt(CPUAlphaState * env,uint64_t a)496 uint64_t helper_cvtqt(CPUAlphaState *env, uint64_t a)
497 {
498 float64 fr = int64_to_float64(a, &FP_STATUS);
499 env->error_code = soft_to_fpcr_exc(env);
500 return float64_to_t(fr);
501 }
502
helper_cvtql(CPUAlphaState * env,uint64_t val)503 uint64_t helper_cvtql(CPUAlphaState *env, uint64_t val)
504 {
505 uint32_t exc = 0;
506 if (val != (int32_t)val) {
507 exc = FPCR_IOV | FPCR_INE;
508 }
509 env->error_code = exc;
510
511 return ((val & 0xc0000000) << 32) | ((val & 0x3fffffff) << 29);
512 }
513