2 * Helpers for floating point instructions.
4 * Copyright (c) 2007 Jocelyn Mayer
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.
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.
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/>.
22 #include "softfloat.h"
24 #define FP_STATUS (env->fp_status)
27 void helper_setroundmode(CPUAlphaState
*env
, uint32_t val
)
29 set_float_rounding_mode(val
, &FP_STATUS
);
32 void helper_setflushzero(CPUAlphaState
*env
, uint32_t val
)
34 set_flush_to_zero(val
, &FP_STATUS
);
37 void helper_fp_exc_clear(CPUAlphaState
*env
)
39 set_float_exception_flags(0, &FP_STATUS
);
42 uint32_t helper_fp_exc_get(CPUAlphaState
*env
)
44 return get_float_exception_flags(&FP_STATUS
);
47 static inline void inline_fp_exc_raise(CPUAlphaState
*env
, void *retaddr
,
48 uint32_t exc
, uint32_t regno
)
53 if (exc
& float_flag_invalid
) {
56 if (exc
& float_flag_divbyzero
) {
59 if (exc
& float_flag_overflow
) {
62 if (exc
& float_flag_underflow
) {
65 if (exc
& float_flag_inexact
) {
69 arith_excp(env
, retaddr
, hw_exc
, 1ull << regno
);
73 /* Raise exceptions for ieee fp insns without software completion.
74 In that case there are no exceptions that don't trap; the mask
76 void helper_fp_exc_raise(CPUAlphaState
*env
, uint32_t exc
, uint32_t regno
)
78 inline_fp_exc_raise(env
, GETPC(), exc
, regno
);
81 /* Raise exceptions for ieee fp insns with software completion. */
82 void helper_fp_exc_raise_s(CPUAlphaState
*env
, uint32_t exc
, uint32_t regno
)
85 env
->fpcr_exc_status
|= exc
;
86 exc
&= ~env
->fpcr_exc_mask
;
87 inline_fp_exc_raise(env
, GETPC(), exc
, regno
);
91 /* Input remapping without software completion. Handle denormal-map-to-zero
92 and trap for all other non-finite numbers. */
93 uint64_t helper_ieee_input(CPUAlphaState
*env
, uint64_t val
)
95 uint32_t exp
= (uint32_t)(val
>> 52) & 0x7ff;
96 uint64_t frac
= val
& 0xfffffffffffffull
;
100 /* If DNZ is set flush denormals to zero on input. */
104 arith_excp(env
, GETPC(), EXC_M_UNF
, 0);
107 } else if (exp
== 0x7ff) {
108 /* Infinity or NaN. */
109 /* ??? I'm not sure these exception bit flags are correct. I do
110 know that the Linux kernel, at least, doesn't rely on them and
111 just emulates the insn to figure out what exception to use. */
112 arith_excp(env
, GETPC(), frac
? EXC_M_INV
: EXC_M_FOV
, 0);
117 /* Similar, but does not trap for infinities. Used for comparisons. */
118 uint64_t helper_ieee_input_cmp(CPUAlphaState
*env
, uint64_t val
)
120 uint32_t exp
= (uint32_t)(val
>> 52) & 0x7ff;
121 uint64_t frac
= val
& 0xfffffffffffffull
;
125 /* If DNZ is set flush denormals to zero on input. */
129 arith_excp(env
, GETPC(), EXC_M_UNF
, 0);
132 } else if (exp
== 0x7ff && frac
) {
134 arith_excp(env
, GETPC(), EXC_M_INV
, 0);
139 /* Input remapping with software completion enabled. All we have to do
140 is handle denormal-map-to-zero; all other inputs get exceptions as
141 needed from the actual operation. */
142 uint64_t helper_ieee_input_s(CPUAlphaState
*env
, uint64_t val
)
145 uint32_t exp
= (uint32_t)(val
>> 52) & 0x7ff;
153 /* F floating (VAX) */
154 static uint64_t float32_to_f(float32 fa
)
156 uint64_t r
, exp
, mant
, sig
;
160 sig
= ((uint64_t)a
.l
& 0x80000000) << 32;
161 exp
= (a
.l
>> 23) & 0xff;
162 mant
= ((uint64_t)a
.l
& 0x007fffff) << 29;
165 /* NaN or infinity */
166 r
= 1; /* VAX dirty zero */
167 } else if (exp
== 0) {
173 r
= sig
| ((exp
+ 1) << 52) | mant
;
178 r
= 1; /* VAX dirty zero */
180 r
= sig
| ((exp
+ 2) << 52);
187 static float32
f_to_float32(CPUAlphaState
*env
, void *retaddr
, uint64_t a
)
189 uint32_t exp
, mant_sig
;
192 exp
= ((a
>> 55) & 0x80) | ((a
>> 52) & 0x7f);
193 mant_sig
= ((a
>> 32) & 0x80000000) | ((a
>> 29) & 0x007fffff);
195 if (unlikely(!exp
&& mant_sig
)) {
196 /* Reserved operands / Dirty zero */
197 dynamic_excp(env
, retaddr
, EXCP_OPCDEC
, 0);
204 r
.l
= ((exp
- 2) << 23) | mant_sig
;
210 uint32_t helper_f_to_memory(uint64_t a
)
213 r
= (a
& 0x00001fffe0000000ull
) >> 13;
214 r
|= (a
& 0x07ffe00000000000ull
) >> 45;
215 r
|= (a
& 0xc000000000000000ull
) >> 48;
219 uint64_t helper_memory_to_f(uint32_t a
)
222 r
= ((uint64_t)(a
& 0x0000c000)) << 48;
223 r
|= ((uint64_t)(a
& 0x003fffff)) << 45;
224 r
|= ((uint64_t)(a
& 0xffff0000)) << 13;
225 if (!(a
& 0x00004000)) {
231 /* ??? Emulating VAX arithmetic with IEEE arithmetic is wrong. We should
232 either implement VAX arithmetic properly or just signal invalid opcode. */
234 uint64_t helper_addf(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
238 fa
= f_to_float32(env
, GETPC(), a
);
239 fb
= f_to_float32(env
, GETPC(), b
);
240 fr
= float32_add(fa
, fb
, &FP_STATUS
);
241 return float32_to_f(fr
);
244 uint64_t helper_subf(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
248 fa
= f_to_float32(env
, GETPC(), a
);
249 fb
= f_to_float32(env
, GETPC(), b
);
250 fr
= float32_sub(fa
, fb
, &FP_STATUS
);
251 return float32_to_f(fr
);
254 uint64_t helper_mulf(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
258 fa
= f_to_float32(env
, GETPC(), a
);
259 fb
= f_to_float32(env
, GETPC(), b
);
260 fr
= float32_mul(fa
, fb
, &FP_STATUS
);
261 return float32_to_f(fr
);
264 uint64_t helper_divf(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
268 fa
= f_to_float32(env
, GETPC(), a
);
269 fb
= f_to_float32(env
, GETPC(), b
);
270 fr
= float32_div(fa
, fb
, &FP_STATUS
);
271 return float32_to_f(fr
);
274 uint64_t helper_sqrtf(CPUAlphaState
*env
, uint64_t t
)
278 ft
= f_to_float32(env
, GETPC(), t
);
279 fr
= float32_sqrt(ft
, &FP_STATUS
);
280 return float32_to_f(fr
);
284 /* G floating (VAX) */
285 static uint64_t float64_to_g(float64 fa
)
287 uint64_t r
, exp
, mant
, sig
;
291 sig
= a
.ll
& 0x8000000000000000ull
;
292 exp
= (a
.ll
>> 52) & 0x7ff;
293 mant
= a
.ll
& 0x000fffffffffffffull
;
296 /* NaN or infinity */
297 r
= 1; /* VAX dirty zero */
298 } else if (exp
== 0) {
304 r
= sig
| ((exp
+ 1) << 52) | mant
;
309 r
= 1; /* VAX dirty zero */
311 r
= sig
| ((exp
+ 2) << 52);
318 static float64
g_to_float64(CPUAlphaState
*env
, void *retaddr
, uint64_t a
)
320 uint64_t exp
, mant_sig
;
323 exp
= (a
>> 52) & 0x7ff;
324 mant_sig
= a
& 0x800fffffffffffffull
;
326 if (!exp
&& mant_sig
) {
327 /* Reserved operands / Dirty zero */
328 dynamic_excp(env
, retaddr
, EXCP_OPCDEC
, 0);
335 r
.ll
= ((exp
- 2) << 52) | mant_sig
;
341 uint64_t helper_g_to_memory(uint64_t a
)
344 r
= (a
& 0x000000000000ffffull
) << 48;
345 r
|= (a
& 0x00000000ffff0000ull
) << 16;
346 r
|= (a
& 0x0000ffff00000000ull
) >> 16;
347 r
|= (a
& 0xffff000000000000ull
) >> 48;
351 uint64_t helper_memory_to_g(uint64_t a
)
354 r
= (a
& 0x000000000000ffffull
) << 48;
355 r
|= (a
& 0x00000000ffff0000ull
) << 16;
356 r
|= (a
& 0x0000ffff00000000ull
) >> 16;
357 r
|= (a
& 0xffff000000000000ull
) >> 48;
361 uint64_t helper_addg(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
365 fa
= g_to_float64(env
, GETPC(), a
);
366 fb
= g_to_float64(env
, GETPC(), b
);
367 fr
= float64_add(fa
, fb
, &FP_STATUS
);
368 return float64_to_g(fr
);
371 uint64_t helper_subg(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
375 fa
= g_to_float64(env
, GETPC(), a
);
376 fb
= g_to_float64(env
, GETPC(), b
);
377 fr
= float64_sub(fa
, fb
, &FP_STATUS
);
378 return float64_to_g(fr
);
381 uint64_t helper_mulg(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
385 fa
= g_to_float64(env
, GETPC(), a
);
386 fb
= g_to_float64(env
, GETPC(), b
);
387 fr
= float64_mul(fa
, fb
, &FP_STATUS
);
388 return float64_to_g(fr
);
391 uint64_t helper_divg(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
395 fa
= g_to_float64(env
, GETPC(), a
);
396 fb
= g_to_float64(env
, GETPC(), b
);
397 fr
= float64_div(fa
, fb
, &FP_STATUS
);
398 return float64_to_g(fr
);
401 uint64_t helper_sqrtg(CPUAlphaState
*env
, uint64_t a
)
405 fa
= g_to_float64(env
, GETPC(), a
);
406 fr
= float64_sqrt(fa
, &FP_STATUS
);
407 return float64_to_g(fr
);
411 /* S floating (single) */
413 /* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg. */
414 static inline uint64_t float32_to_s_int(uint32_t fi
)
416 uint32_t frac
= fi
& 0x7fffff;
417 uint32_t sign
= fi
>> 31;
418 uint32_t exp_msb
= (fi
>> 30) & 1;
419 uint32_t exp_low
= (fi
>> 23) & 0x7f;
422 exp
= (exp_msb
<< 10) | exp_low
;
424 if (exp_low
== 0x7f) {
428 if (exp_low
!= 0x00) {
433 return (((uint64_t)sign
<< 63)
434 | ((uint64_t)exp
<< 52)
435 | ((uint64_t)frac
<< 29));
438 static inline uint64_t float32_to_s(float32 fa
)
442 return float32_to_s_int(a
.l
);
445 static inline uint32_t s_to_float32_int(uint64_t a
)
447 return ((a
>> 32) & 0xc0000000) | ((a
>> 29) & 0x3fffffff);
450 static inline float32
s_to_float32(uint64_t a
)
453 r
.l
= s_to_float32_int(a
);
457 uint32_t helper_s_to_memory(uint64_t a
)
459 return s_to_float32_int(a
);
462 uint64_t helper_memory_to_s(uint32_t a
)
464 return float32_to_s_int(a
);
467 uint64_t helper_adds(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
471 fa
= s_to_float32(a
);
472 fb
= s_to_float32(b
);
473 fr
= float32_add(fa
, fb
, &FP_STATUS
);
474 return float32_to_s(fr
);
477 uint64_t helper_subs(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
481 fa
= s_to_float32(a
);
482 fb
= s_to_float32(b
);
483 fr
= float32_sub(fa
, fb
, &FP_STATUS
);
484 return float32_to_s(fr
);
487 uint64_t helper_muls(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
491 fa
= s_to_float32(a
);
492 fb
= s_to_float32(b
);
493 fr
= float32_mul(fa
, fb
, &FP_STATUS
);
494 return float32_to_s(fr
);
497 uint64_t helper_divs(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
501 fa
= s_to_float32(a
);
502 fb
= s_to_float32(b
);
503 fr
= float32_div(fa
, fb
, &FP_STATUS
);
504 return float32_to_s(fr
);
507 uint64_t helper_sqrts(CPUAlphaState
*env
, uint64_t a
)
511 fa
= s_to_float32(a
);
512 fr
= float32_sqrt(fa
, &FP_STATUS
);
513 return float32_to_s(fr
);
517 /* T floating (double) */
518 static inline float64
t_to_float64(uint64_t a
)
520 /* Memory format is the same as float64 */
526 static inline uint64_t float64_to_t(float64 fa
)
528 /* Memory format is the same as float64 */
534 uint64_t helper_addt(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
538 fa
= t_to_float64(a
);
539 fb
= t_to_float64(b
);
540 fr
= float64_add(fa
, fb
, &FP_STATUS
);
541 return float64_to_t(fr
);
544 uint64_t helper_subt(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
548 fa
= t_to_float64(a
);
549 fb
= t_to_float64(b
);
550 fr
= float64_sub(fa
, fb
, &FP_STATUS
);
551 return float64_to_t(fr
);
554 uint64_t helper_mult(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
558 fa
= t_to_float64(a
);
559 fb
= t_to_float64(b
);
560 fr
= float64_mul(fa
, fb
, &FP_STATUS
);
561 return float64_to_t(fr
);
564 uint64_t helper_divt(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
568 fa
= t_to_float64(a
);
569 fb
= t_to_float64(b
);
570 fr
= float64_div(fa
, fb
, &FP_STATUS
);
571 return float64_to_t(fr
);
574 uint64_t helper_sqrtt(CPUAlphaState
*env
, uint64_t a
)
578 fa
= t_to_float64(a
);
579 fr
= float64_sqrt(fa
, &FP_STATUS
);
580 return float64_to_t(fr
);
584 uint64_t helper_cmptun(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
588 fa
= t_to_float64(a
);
589 fb
= t_to_float64(b
);
591 if (float64_unordered_quiet(fa
, fb
, &FP_STATUS
)) {
592 return 0x4000000000000000ULL
;
598 uint64_t helper_cmpteq(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
602 fa
= t_to_float64(a
);
603 fb
= t_to_float64(b
);
605 if (float64_eq_quiet(fa
, fb
, &FP_STATUS
)) {
606 return 0x4000000000000000ULL
;
612 uint64_t helper_cmptle(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
616 fa
= t_to_float64(a
);
617 fb
= t_to_float64(b
);
619 if (float64_le(fa
, fb
, &FP_STATUS
)) {
620 return 0x4000000000000000ULL
;
626 uint64_t helper_cmptlt(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
630 fa
= t_to_float64(a
);
631 fb
= t_to_float64(b
);
633 if (float64_lt(fa
, fb
, &FP_STATUS
)) {
634 return 0x4000000000000000ULL
;
640 uint64_t helper_cmpgeq(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
644 fa
= g_to_float64(env
, GETPC(), a
);
645 fb
= g_to_float64(env
, GETPC(), b
);
647 if (float64_eq_quiet(fa
, fb
, &FP_STATUS
)) {
648 return 0x4000000000000000ULL
;
654 uint64_t helper_cmpgle(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
658 fa
= g_to_float64(env
, GETPC(), a
);
659 fb
= g_to_float64(env
, GETPC(), b
);
661 if (float64_le(fa
, fb
, &FP_STATUS
)) {
662 return 0x4000000000000000ULL
;
668 uint64_t helper_cmpglt(CPUAlphaState
*env
, uint64_t a
, uint64_t b
)
672 fa
= g_to_float64(env
, GETPC(), a
);
673 fb
= g_to_float64(env
, GETPC(), b
);
675 if (float64_lt(fa
, fb
, &FP_STATUS
)) {
676 return 0x4000000000000000ULL
;
682 /* Floating point format conversion */
683 uint64_t helper_cvtts(CPUAlphaState
*env
, uint64_t a
)
688 fa
= t_to_float64(a
);
689 fr
= float64_to_float32(fa
, &FP_STATUS
);
690 return float32_to_s(fr
);
693 uint64_t helper_cvtst(CPUAlphaState
*env
, uint64_t a
)
698 fa
= s_to_float32(a
);
699 fr
= float32_to_float64(fa
, &FP_STATUS
);
700 return float64_to_t(fr
);
703 uint64_t helper_cvtqs(CPUAlphaState
*env
, uint64_t a
)
705 float32 fr
= int64_to_float32(a
, &FP_STATUS
);
706 return float32_to_s(fr
);
709 /* Implement float64 to uint64 conversion without saturation -- we must
710 supply the truncated result. This behaviour is used by the compiler
711 to get unsigned conversion for free with the same instruction.
713 The VI flag is set when overflow or inexact exceptions should be raised. */
715 static inline uint64_t inline_cvttq(CPUAlphaState
*env
, uint64_t a
,
716 int roundmode
, int VI
)
718 uint64_t frac
, ret
= 0;
719 uint32_t exp
, sign
, exc
= 0;
723 exp
= (uint32_t)(a
>> 52) & 0x7ff;
724 frac
= a
& 0xfffffffffffffull
;
727 if (unlikely(frac
!= 0)) {
730 } else if (exp
== 0x7ff) {
731 exc
= (frac
? float_flag_invalid
: VI
? float_flag_overflow
: 0);
733 /* Restore implicit bit. */
734 frac
|= 0x10000000000000ull
;
736 shift
= exp
- 1023 - 52;
738 /* In this case the number is so large that we must shift
739 the fraction left. There is no rounding to do. */
742 if (VI
&& (ret
>> shift
) != frac
) {
743 exc
= float_flag_overflow
;
749 /* In this case the number is smaller than the fraction as
750 represented by the 52 bit number. Here we must think
751 about rounding the result. Handle this by shifting the
752 fractional part of the number into the high bits of ROUND.
753 This will let us efficiently handle round-to-nearest. */
757 round
= frac
<< (64 - shift
);
759 /* The exponent is so small we shift out everything.
760 Leave a sticky bit for proper rounding below. */
766 exc
= (VI
? float_flag_inexact
: 0);
768 case float_round_nearest_even
:
769 if (round
== (1ull << 63)) {
770 /* Fraction is exactly 0.5; round to even. */
772 } else if (round
> (1ull << 63)) {
776 case float_round_to_zero
:
781 case float_round_down
:
792 float_raise(exc
, &FP_STATUS
);
798 uint64_t helper_cvttq(CPUAlphaState
*env
, uint64_t a
)
800 return inline_cvttq(env
, a
, FP_STATUS
.float_rounding_mode
, 1);
803 uint64_t helper_cvttq_c(CPUAlphaState
*env
, uint64_t a
)
805 return inline_cvttq(env
, a
, float_round_to_zero
, 0);
808 uint64_t helper_cvttq_svic(CPUAlphaState
*env
, uint64_t a
)
810 return inline_cvttq(env
, a
, float_round_to_zero
, 1);
813 uint64_t helper_cvtqt(CPUAlphaState
*env
, uint64_t a
)
815 float64 fr
= int64_to_float64(a
, &FP_STATUS
);
816 return float64_to_t(fr
);
819 uint64_t helper_cvtqf(CPUAlphaState
*env
, uint64_t a
)
821 float32 fr
= int64_to_float32(a
, &FP_STATUS
);
822 return float32_to_f(fr
);
825 uint64_t helper_cvtgf(CPUAlphaState
*env
, uint64_t a
)
830 fa
= g_to_float64(env
, GETPC(), a
);
831 fr
= float64_to_float32(fa
, &FP_STATUS
);
832 return float32_to_f(fr
);
835 uint64_t helper_cvtgq(CPUAlphaState
*env
, uint64_t a
)
837 float64 fa
= g_to_float64(env
, GETPC(), a
);
838 return float64_to_int64_round_to_zero(fa
, &FP_STATUS
);
841 uint64_t helper_cvtqg(CPUAlphaState
*env
, uint64_t a
)
844 fr
= int64_to_float64(a
, &FP_STATUS
);
845 return float64_to_g(fr
);