xref: /openbmc/qemu/target/alpha/fpu_helper.c (revision 5ade579b)
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 
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 &= 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.  */
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.  */
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.  */
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.  */
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 
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 
194 static inline uint32_t s_to_float32_int(uint64_t a)
195 {
196     return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff);
197 }
198 
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 
206 uint32_t helper_s_to_memory(uint64_t a)
207 {
208     return s_to_float32_int(a);
209 }
210 
211 uint64_t helper_memory_to_s(uint32_t a)
212 {
213     return float32_to_s_int(a);
214 }
215 
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 
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 
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 
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 
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) */
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 
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 
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 
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 
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 
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 
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 */
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 
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 
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 
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 */
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 
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 
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 
454 static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode)
455 {
456     uint64_t frac, ret = 0;
457     uint32_t exp, sign, exc = 0;
458     int shift;
459 
460     sign = (a >> 63);
461     exp = (uint32_t)(a >> 52) & 0x7ff;
462     frac = a & 0xfffffffffffffull;
463 
464     if (exp == 0) {
465         if (unlikely(frac != 0) && !env->fp_status.flush_inputs_to_zero) {
466             goto do_underflow;
467         }
468     } else if (exp == 0x7ff) {
469         exc = FPCR_INV;
470     } else {
471         /* Restore implicit bit.  */
472         frac |= 0x10000000000000ull;
473 
474         shift = exp - 1023 - 52;
475         if (shift >= 0) {
476             /* In this case the number is so large that we must shift
477                the fraction left.  There is no rounding to do.  */
478             if (shift < 64) {
479                 ret = frac << shift;
480             }
481             /* Check for overflow.  Note the special case of -0x1p63.  */
482             if (shift >= 11 && a != 0xC3E0000000000000ull) {
483                 exc = FPCR_IOV | FPCR_INE;
484             }
485         } else {
486             uint64_t round;
487 
488             /* In this case the number is smaller than the fraction as
489                represented by the 52 bit number.  Here we must think
490                about rounding the result.  Handle this by shifting the
491                fractional part of the number into the high bits of ROUND.
492                This will let us efficiently handle round-to-nearest.  */
493             shift = -shift;
494             if (shift < 63) {
495                 ret = frac >> shift;
496                 round = frac << (64 - shift);
497             } else {
498                 /* The exponent is so small we shift out everything.
499                    Leave a sticky bit for proper rounding below.  */
500             do_underflow:
501                 round = 1;
502             }
503 
504             if (round) {
505                 exc = FPCR_INE;
506                 switch (roundmode) {
507                 case float_round_nearest_even:
508                     if (round == (1ull << 63)) {
509                         /* Fraction is exactly 0.5; round to even.  */
510                         ret += (ret & 1);
511                     } else if (round > (1ull << 63)) {
512                         ret += 1;
513                     }
514                     break;
515                 case float_round_to_zero:
516                     break;
517                 case float_round_up:
518                     ret += 1 - sign;
519                     break;
520                 case float_round_down:
521                     ret += sign;
522                     break;
523                 }
524             }
525         }
526         if (sign) {
527             ret = -ret;
528         }
529     }
530     env->error_code = exc;
531 
532     return ret;
533 }
534 
535 uint64_t helper_cvttq(CPUAlphaState *env, uint64_t a)
536 {
537     return do_cvttq(env, a, FP_STATUS.float_rounding_mode);
538 }
539 
540 uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a)
541 {
542     return do_cvttq(env, a, float_round_to_zero);
543 }
544 
545 uint64_t helper_cvtqt(CPUAlphaState *env, uint64_t a)
546 {
547     float64 fr = int64_to_float64(a, &FP_STATUS);
548     env->error_code = soft_to_fpcr_exc(env);
549     return float64_to_t(fr);
550 }
551 
552 uint64_t helper_cvtql(CPUAlphaState *env, uint64_t val)
553 {
554     uint32_t exc = 0;
555     if (val != (int32_t)val) {
556         exc = FPCR_IOV | FPCR_INE;
557     }
558     env->error_code = exc;
559 
560     return ((val & 0xc0000000) << 32) | ((val & 0x3fffffff) << 29);
561 }
562