xref: /openbmc/qemu/target/alpha/fpu_helper.c (revision 2df1eb27)
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     float64 fa;
457     int64_t ret;
458     uint32_t exc;
459 
460     fa = t_to_float64(a);
461     ret = float64_to_int64_modulo(fa, roundmode, &FP_STATUS);
462 
463     exc = get_float_exception_flags(&FP_STATUS);
464     if (unlikely(exc)) {
465         set_float_exception_flags(0, &FP_STATUS);
466 
467         /* We need to massage the resulting exceptions. */
468         if (exc & float_flag_invalid_cvti) {
469             /* Overflow, either normal or infinity. */
470             if (float64_is_infinity(fa)) {
471                 exc = FPCR_INV;
472             } else {
473                 exc = FPCR_IOV | FPCR_INE;
474             }
475         } else if (exc & float_flag_invalid) {
476             exc = FPCR_INV;
477         } else if (exc & float_flag_inexact) {
478             exc = FPCR_INE;
479         }
480     }
481     env->error_code = exc;
482 
483     return ret;
484 }
485 
486 uint64_t helper_cvttq(CPUAlphaState *env, uint64_t a)
487 {
488     return do_cvttq(env, a, FP_STATUS.float_rounding_mode);
489 }
490 
491 uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a)
492 {
493     return do_cvttq(env, a, float_round_to_zero);
494 }
495 
496 uint64_t helper_cvtqt(CPUAlphaState *env, uint64_t a)
497 {
498     float64 fr = int64_to_float64(a, &FP_STATUS);
499     env->error_code = soft_to_fpcr_exc(env);
500     return float64_to_t(fr);
501 }
502 
503 uint64_t helper_cvtql(CPUAlphaState *env, uint64_t val)
504 {
505     uint32_t exc = 0;
506     if (val != (int32_t)val) {
507         exc = FPCR_IOV | FPCR_INE;
508     }
509     env->error_code = exc;
510 
511     return ((val & 0xc0000000) << 32) | ((val & 0x3fffffff) << 29);
512 }
513