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 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 | |
29 | void helper_setroundmode(CPUAlphaState *env, uint32_t val) |
30 | { |
31 | set_float_rounding_mode(val, &FP_STATUS); |
32 | } |
33 | |
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 | |
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 | |
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. */ |
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. */ |
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 &= ~ignore; |
94 | #ifdef CONFIG_USER_ONLY |
95 | /* |
96 | * In user mode, the kernel's software handler only |
97 | * delivers a signal if the exception is enabled. |
98 | */ |
99 | if (!(exc & env->fpcr_exc_enable)) { |
100 | return; |
101 | } |
102 | #else |
103 | /* |
104 | * In system mode, the software handler gets invoked |
105 | * for any non-ignored exception. |
106 | */ |
107 | if (!exc) { |
108 | return; |
109 | } |
110 | #endif |
111 | exc &= env->fpcr_exc_enable; |
112 | fp_exc_raise1(env, GETPC(), exc, regno, EXC_M_SWC); |
113 | } |
114 | } |
115 | |
116 | /* Input handing without software completion. Trap for all |
117 | non-finite numbers. */ |
118 | void helper_ieee_input(CPUAlphaState *env, uint64_t val) |
119 | { |
120 | uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; |
121 | uint64_t frac = val & 0xfffffffffffffull; |
122 | |
123 | if (exp == 0) { |
124 | /* Denormals without /S raise an exception. */ |
125 | if (frac != 0) { |
126 | arith_excp(env, GETPC(), EXC_M_INV, 0); |
127 | } |
128 | } else if (exp == 0x7ff) { |
129 | /* Infinity or NaN. */ |
130 | env->fpcr |= FPCR_INV; |
131 | arith_excp(env, GETPC(), EXC_M_INV, 0); |
132 | } |
133 | } |
134 | |
135 | /* Similar, but does not trap for infinities. Used for comparisons. */ |
136 | void helper_ieee_input_cmp(CPUAlphaState *env, uint64_t val) |
137 | { |
138 | uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; |
139 | uint64_t frac = val & 0xfffffffffffffull; |
140 | |
141 | if (exp == 0) { |
142 | /* Denormals without /S raise an exception. */ |
143 | if (frac != 0) { |
144 | arith_excp(env, GETPC(), EXC_M_INV, 0); |
145 | } |
146 | } else if (exp == 0x7ff && frac) { |
147 | /* NaN. */ |
148 | env->fpcr |= FPCR_INV; |
149 | arith_excp(env, GETPC(), EXC_M_INV, 0); |
150 | } |
151 | } |
152 | |
153 | /* Input handing with software completion. Trap for denorms, unless DNZ |
154 | is set. If we try to support DNOD (which none of the produced hardware |
155 | did, AFAICS), we'll need to suppress the trap when FPCR.DNOD is set; |
156 | then the code downstream of that will need to cope with denorms sans |
157 | flush_input_to_zero. Most of it should work sanely, but there's |
158 | nothing to compare with. */ |
159 | void helper_ieee_input_s(CPUAlphaState *env, uint64_t val) |
160 | { |
161 | if (unlikely(2 * val - 1 < 0x1fffffffffffffull) |
162 | && !env->fp_status.flush_inputs_to_zero) { |
163 | arith_excp(env, GETPC(), EXC_M_INV | EXC_M_SWC, 0); |
164 | } |
165 | } |
166 | |
167 | /* S floating (single) */ |
168 | |
169 | /* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg. */ |
170 | static inline uint64_t float32_to_s_int(uint32_t fi) |
171 | { |
172 | uint32_t frac = fi & 0x7fffff; |
173 | uint32_t sign = fi >> 31; |
174 | uint32_t exp_msb = (fi >> 30) & 1; |
175 | uint32_t exp_low = (fi >> 23) & 0x7f; |
176 | uint32_t exp; |
177 | |
178 | exp = (exp_msb << 10) | exp_low; |
179 | if (exp_msb) { |
180 | if (exp_low == 0x7f) { |
181 | exp = 0x7ff; |
182 | } |
183 | } else { |
184 | if (exp_low != 0x00) { |
185 | exp |= 0x380; |
186 | } |
187 | } |
188 | |
189 | return (((uint64_t)sign << 63) |
190 | | ((uint64_t)exp << 52) |
191 | | ((uint64_t)frac << 29)); |
192 | } |
193 | |
194 | static inline uint64_t float32_to_s(float32 fa) |
195 | { |
196 | CPU_FloatU a; |
197 | a.f = fa; |
198 | return float32_to_s_int(a.l); |
199 | } |
200 | |
201 | static inline uint32_t s_to_float32_int(uint64_t a) |
202 | { |
203 | return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff); |
204 | } |
205 | |
206 | static inline float32 s_to_float32(uint64_t a) |
207 | { |
208 | CPU_FloatU r; |
209 | r.l = s_to_float32_int(a); |
210 | return r.f; |
211 | } |
212 | |
213 | uint32_t helper_s_to_memory(uint64_t a) |
214 | { |
215 | return s_to_float32_int(a); |
216 | } |
217 | |
218 | uint64_t helper_memory_to_s(uint32_t a) |
219 | { |
220 | return float32_to_s_int(a); |
221 | } |
222 | |
223 | uint64_t helper_adds(CPUAlphaState *env, uint64_t a, uint64_t b) |
224 | { |
225 | float32 fa, fb, fr; |
226 | |
227 | fa = s_to_float32(a); |
228 | fb = s_to_float32(b); |
229 | fr = float32_add(fa, fb, &FP_STATUS); |
230 | env->error_code = soft_to_fpcr_exc(env); |
231 | |
232 | return float32_to_s(fr); |
233 | } |
234 | |
235 | uint64_t helper_subs(CPUAlphaState *env, uint64_t a, uint64_t b) |
236 | { |
237 | float32 fa, fb, fr; |
238 | |
239 | fa = s_to_float32(a); |
240 | fb = s_to_float32(b); |
241 | fr = float32_sub(fa, fb, &FP_STATUS); |
242 | env->error_code = soft_to_fpcr_exc(env); |
243 | |
244 | return float32_to_s(fr); |
245 | } |
246 | |
247 | uint64_t helper_muls(CPUAlphaState *env, uint64_t a, uint64_t b) |
248 | { |
249 | float32 fa, fb, fr; |
250 | |
251 | fa = s_to_float32(a); |
252 | fb = s_to_float32(b); |
253 | fr = float32_mul(fa, fb, &FP_STATUS); |
254 | env->error_code = soft_to_fpcr_exc(env); |
255 | |
256 | return float32_to_s(fr); |
257 | } |
258 | |
259 | uint64_t helper_divs(CPUAlphaState *env, uint64_t a, uint64_t b) |
260 | { |
261 | float32 fa, fb, fr; |
262 | |
263 | fa = s_to_float32(a); |
264 | fb = s_to_float32(b); |
265 | fr = float32_div(fa, fb, &FP_STATUS); |
266 | env->error_code = soft_to_fpcr_exc(env); |
267 | |
268 | return float32_to_s(fr); |
269 | } |
270 | |
271 | uint64_t helper_sqrts(CPUAlphaState *env, uint64_t a) |
272 | { |
273 | float32 fa, fr; |
274 | |
275 | fa = s_to_float32(a); |
276 | fr = float32_sqrt(fa, &FP_STATUS); |
277 | env->error_code = soft_to_fpcr_exc(env); |
278 | |
279 | return float32_to_s(fr); |
280 | } |
281 | |
282 | |
283 | /* T floating (double) */ |
284 | static inline float64 t_to_float64(uint64_t a) |
285 | { |
286 | /* Memory format is the same as float64 */ |
287 | CPU_DoubleU r; |
288 | r.ll = a; |
289 | return r.d; |
290 | } |
291 | |
292 | static inline uint64_t float64_to_t(float64 fa) |
293 | { |
294 | /* Memory format is the same as float64 */ |
295 | CPU_DoubleU r; |
296 | r.d = fa; |
297 | return r.ll; |
298 | } |
299 | |
300 | uint64_t helper_addt(CPUAlphaState *env, uint64_t a, uint64_t b) |
301 | { |
302 | float64 fa, fb, fr; |
303 | |
304 | fa = t_to_float64(a); |
305 | fb = t_to_float64(b); |
306 | fr = float64_add(fa, fb, &FP_STATUS); |
307 | env->error_code = soft_to_fpcr_exc(env); |
308 | |
309 | return float64_to_t(fr); |
310 | } |
311 | |
312 | uint64_t helper_subt(CPUAlphaState *env, uint64_t a, uint64_t b) |
313 | { |
314 | float64 fa, fb, fr; |
315 | |
316 | fa = t_to_float64(a); |
317 | fb = t_to_float64(b); |
318 | fr = float64_sub(fa, fb, &FP_STATUS); |
319 | env->error_code = soft_to_fpcr_exc(env); |
320 | |
321 | return float64_to_t(fr); |
322 | } |
323 | |
324 | uint64_t helper_mult(CPUAlphaState *env, uint64_t a, uint64_t b) |
325 | { |
326 | float64 fa, fb, fr; |
327 | |
328 | fa = t_to_float64(a); |
329 | fb = t_to_float64(b); |
330 | fr = float64_mul(fa, fb, &FP_STATUS); |
331 | env->error_code = soft_to_fpcr_exc(env); |
332 | |
333 | return float64_to_t(fr); |
334 | } |
335 | |
336 | uint64_t helper_divt(CPUAlphaState *env, uint64_t a, uint64_t b) |
337 | { |
338 | float64 fa, fb, fr; |
339 | |
340 | fa = t_to_float64(a); |
341 | fb = t_to_float64(b); |
342 | fr = float64_div(fa, fb, &FP_STATUS); |
343 | env->error_code = soft_to_fpcr_exc(env); |
344 | |
345 | return float64_to_t(fr); |
346 | } |
347 | |
348 | uint64_t helper_sqrtt(CPUAlphaState *env, uint64_t a) |
349 | { |
350 | float64 fa, fr; |
351 | |
352 | fa = t_to_float64(a); |
353 | fr = float64_sqrt(fa, &FP_STATUS); |
354 | env->error_code = soft_to_fpcr_exc(env); |
355 | |
356 | return float64_to_t(fr); |
357 | } |
358 | |
359 | /* Comparisons */ |
360 | uint64_t helper_cmptun(CPUAlphaState *env, uint64_t a, uint64_t b) |
361 | { |
362 | float64 fa, fb; |
363 | uint64_t ret = 0; |
364 | |
365 | fa = t_to_float64(a); |
366 | fb = t_to_float64(b); |
367 | |
368 | if (float64_unordered_quiet(fa, fb, &FP_STATUS)) { |
369 | ret = 0x4000000000000000ULL; |
370 | } |
371 | env->error_code = soft_to_fpcr_exc(env); |
372 | |
373 | return ret; |
374 | } |
375 | |
376 | uint64_t helper_cmpteq(CPUAlphaState *env, uint64_t a, uint64_t b) |
377 | { |
378 | float64 fa, fb; |
379 | uint64_t ret = 0; |
380 | |
381 | fa = t_to_float64(a); |
382 | fb = t_to_float64(b); |
383 | |
384 | if (float64_eq_quiet(fa, fb, &FP_STATUS)) { |
385 | ret = 0x4000000000000000ULL; |
386 | } |
387 | env->error_code = soft_to_fpcr_exc(env); |
388 | |
389 | return ret; |
390 | } |
391 | |
392 | uint64_t helper_cmptle(CPUAlphaState *env, uint64_t a, uint64_t b) |
393 | { |
394 | float64 fa, fb; |
395 | uint64_t ret = 0; |
396 | |
397 | fa = t_to_float64(a); |
398 | fb = t_to_float64(b); |
399 | |
400 | if (float64_le(fa, fb, &FP_STATUS)) { |
401 | ret = 0x4000000000000000ULL; |
402 | } |
403 | env->error_code = soft_to_fpcr_exc(env); |
404 | |
405 | return ret; |
406 | } |
407 | |
408 | uint64_t helper_cmptlt(CPUAlphaState *env, uint64_t a, uint64_t b) |
409 | { |
410 | float64 fa, fb; |
411 | uint64_t ret = 0; |
412 | |
413 | fa = t_to_float64(a); |
414 | fb = t_to_float64(b); |
415 | |
416 | if (float64_lt(fa, fb, &FP_STATUS)) { |
417 | ret = 0x4000000000000000ULL; |
418 | } |
419 | env->error_code = soft_to_fpcr_exc(env); |
420 | |
421 | return ret; |
422 | } |
423 | |
424 | /* Floating point format conversion */ |
425 | uint64_t helper_cvtts(CPUAlphaState *env, uint64_t a) |
426 | { |
427 | float64 fa; |
428 | float32 fr; |
429 | |
430 | fa = t_to_float64(a); |
431 | fr = float64_to_float32(fa, &FP_STATUS); |
432 | env->error_code = soft_to_fpcr_exc(env); |
433 | |
434 | return float32_to_s(fr); |
435 | } |
436 | |
437 | uint64_t helper_cvtst(CPUAlphaState *env, uint64_t a) |
438 | { |
439 | float32 fa; |
440 | float64 fr; |
441 | |
442 | fa = s_to_float32(a); |
443 | fr = float32_to_float64(fa, &FP_STATUS); |
444 | env->error_code = soft_to_fpcr_exc(env); |
445 | |
446 | return float64_to_t(fr); |
447 | } |
448 | |
449 | uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a) |
450 | { |
451 | float32 fr = int64_to_float32(a, &FP_STATUS); |
452 | env->error_code = soft_to_fpcr_exc(env); |
453 | |
454 | return float32_to_s(fr); |
455 | } |
456 | |
457 | /* Implement float64 to uint64_t conversion without saturation -- we must |
458 | supply the truncated result. This behaviour is used by the compiler |
459 | to get unsigned conversion for free with the same instruction. */ |
460 | |
461 | static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode) |
462 | { |
463 | uint64_t frac, ret = 0; |
464 | uint32_t exp, sign, exc = 0; |
465 | int shift; |
466 | |
467 | sign = (a >> 63); |
468 | exp = (uint32_t)(a >> 52) & 0x7ff; |
469 | frac = a & 0xfffffffffffffull; |
470 | |
471 | if (exp == 0) { |
472 | if (unlikely(frac != 0) && !env->fp_status.flush_inputs_to_zero) { |
473 | goto do_underflow; |
474 | } |
475 | } else if (exp == 0x7ff) { |
476 | exc = FPCR_INV; |
477 | } else { |
478 | /* Restore implicit bit. */ |
479 | frac |= 0x10000000000000ull; |
480 | |
481 | shift = exp - 1023 - 52; |
482 | if (shift >= 0) { |
483 | /* In this case the number is so large that we must shift |
484 | the fraction left. There is no rounding to do. */ |
485 | if (shift < 64) { |
486 | ret = frac << shift; |
487 | } |
488 | /* Check for overflow. Note the special case of -0x1p63. */ |
489 | if (shift >= 11 && a != 0xC3E0000000000000ull) { |
490 | exc = FPCR_IOV | FPCR_INE; |
491 | } |
492 | } else { |
493 | uint64_t round; |
494 | |
495 | /* In this case the number is smaller than the fraction as |
496 | represented by the 52 bit number. Here we must think |
497 | about rounding the result. Handle this by shifting the |
498 | fractional part of the number into the high bits of ROUND. |
499 | This will let us efficiently handle round-to-nearest. */ |
500 | shift = -shift; |
501 | if (shift < 63) { |
502 | ret = frac >> shift; |
503 | round = frac << (64 - shift); |
504 | } else { |
505 | /* The exponent is so small we shift out everything. |
506 | Leave a sticky bit for proper rounding below. */ |
507 | do_underflow: |
508 | round = 1; |
509 | } |
510 | |
511 | if (round) { |
512 | exc = FPCR_INE; |
513 | switch (roundmode) { |
514 | case float_round_nearest_even: |
515 | if (round == (1ull << 63)) { |
516 | /* Fraction is exactly 0.5; round to even. */ |
517 | ret += (ret & 1); |
518 | } else if (round > (1ull << 63)) { |
519 | ret += 1; |
520 | } |
521 | break; |
522 | case float_round_to_zero: |
523 | break; |
524 | case float_round_up: |
525 | ret += 1 - sign; |
526 | break; |
527 | case float_round_down: |
528 | ret += sign; |
529 | break; |
530 | } |
531 | } |
532 | } |
533 | if (sign) { |
534 | ret = -ret; |
535 | } |
536 | } |
537 | env->error_code = exc; |
538 | |
539 | return ret; |
540 | } |
541 | |
542 | uint64_t helper_cvttq(CPUAlphaState *env, uint64_t a) |
543 | { |
544 | return do_cvttq(env, a, FP_STATUS.float_rounding_mode); |
545 | } |
546 | |
547 | uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a) |
548 | { |
549 | return do_cvttq(env, a, float_round_to_zero); |
550 | } |
551 | |
552 | uint64_t helper_cvtqt(CPUAlphaState *env, uint64_t a) |
553 | { |
554 | float64 fr = int64_to_float64(a, &FP_STATUS); |
555 | env->error_code = soft_to_fpcr_exc(env); |
556 | return float64_to_t(fr); |
557 | } |
558 | |
559 | uint64_t helper_cvtql(CPUAlphaState *env, uint64_t val) |
560 | { |
561 | uint32_t exc = 0; |
562 | if (val != (int32_t)val) { |
563 | exc = FPCR_IOV | FPCR_INE; |
564 | } |
565 | env->error_code = exc; |
566 | |
567 | return ((val & 0xc0000000) << 32) | ((val & 0x3fffffff) << 29); |
568 | } |
569 | |