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