xref: /openbmc/qemu/target/alpha/fpu_helper.c (revision 64552b6b)
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