xref: /qemu/target/alpha/fpu_helper.c (revision 7cef6d686309e2792186504ae17cf4f3eb57ef68)
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/helper-proto.h"
23 #include "fpu/softfloat.h"
24 
25 #define FP_STATUS (env->fp_status)
26 
27 
helper_setroundmode(CPUAlphaState * env,uint32_t val)28 void helper_setroundmode(CPUAlphaState *env, uint32_t val)
29 {
30     set_float_rounding_mode(val, &FP_STATUS);
31 }
32 
helper_setflushzero(CPUAlphaState * env,uint32_t val)33 void helper_setflushzero(CPUAlphaState *env, uint32_t val)
34 {
35     set_flush_to_zero(val, &FP_STATUS);
36 }
37 
38 #define CONVERT_BIT(X, SRC, DST) \
39     (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
40 
soft_to_fpcr_exc(CPUAlphaState * env)41 static uint32_t soft_to_fpcr_exc(CPUAlphaState *env)
42 {
43     uint8_t exc = get_float_exception_flags(&FP_STATUS);
44     uint32_t ret = 0;
45 
46     if (unlikely(exc)) {
47         set_float_exception_flags(0, &FP_STATUS);
48         ret |= CONVERT_BIT(exc, float_flag_invalid, FPCR_INV);
49         ret |= CONVERT_BIT(exc, float_flag_divbyzero, FPCR_DZE);
50         ret |= CONVERT_BIT(exc, float_flag_overflow, FPCR_OVF);
51         ret |= CONVERT_BIT(exc, float_flag_underflow, FPCR_UNF);
52         ret |= CONVERT_BIT(exc, float_flag_inexact, FPCR_INE);
53     }
54 
55     return ret;
56 }
57 
fp_exc_raise1(CPUAlphaState * env,uintptr_t retaddr,uint32_t exc,uint32_t regno,uint32_t hw_exc)58 static void fp_exc_raise1(CPUAlphaState *env, uintptr_t retaddr,
59                           uint32_t exc, uint32_t regno, uint32_t hw_exc)
60 {
61     hw_exc |= CONVERT_BIT(exc, FPCR_INV, EXC_M_INV);
62     hw_exc |= CONVERT_BIT(exc, FPCR_DZE, EXC_M_DZE);
63     hw_exc |= CONVERT_BIT(exc, FPCR_OVF, EXC_M_FOV);
64     hw_exc |= CONVERT_BIT(exc, FPCR_UNF, EXC_M_UNF);
65     hw_exc |= CONVERT_BIT(exc, FPCR_INE, EXC_M_INE);
66     hw_exc |= CONVERT_BIT(exc, FPCR_IOV, EXC_M_IOV);
67 
68     arith_excp(env, retaddr, hw_exc, 1ull << regno);
69 }
70 
71 /* Raise exceptions for ieee fp insns without software completion.
72    In that case there are no exceptions that don't trap; the mask
73    doesn't apply.  */
helper_fp_exc_raise(CPUAlphaState * env,uint32_t ignore,uint32_t regno)74 void helper_fp_exc_raise(CPUAlphaState *env, uint32_t ignore, uint32_t regno)
75 {
76     uint32_t exc = env->error_code;
77     if (exc) {
78         env->fpcr |= exc;
79         exc &= ~ignore;
80         if (exc) {
81             fp_exc_raise1(env, GETPC(), exc, regno, 0);
82         }
83     }
84 }
85 
86 /* Raise exceptions for ieee fp insns with software completion.  */
helper_fp_exc_raise_s(CPUAlphaState * env,uint32_t ignore,uint32_t regno)87 void helper_fp_exc_raise_s(CPUAlphaState *env, uint32_t ignore, uint32_t regno)
88 {
89     uint32_t exc = env->error_code & ~ignore;
90     if (exc) {
91         env->fpcr |= exc;
92         exc &= env->fpcr_exc_enable;
93         /*
94          * In system mode, the software handler gets invoked
95          * for any non-ignored exception.
96          * In user mode, the kernel's software handler only
97          * delivers a signal if the exception is enabled.
98          */
99 #ifdef CONFIG_USER_ONLY
100         if (!exc) {
101             return;
102         }
103 #endif
104         fp_exc_raise1(env, GETPC(), exc, regno, EXC_M_SWC);
105     }
106 }
107 
108 /* Input handing without software completion.  Trap for all
109    non-finite numbers.  */
helper_ieee_input(CPUAlphaState * env,uint64_t val)110 void helper_ieee_input(CPUAlphaState *env, uint64_t val)
111 {
112     uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
113     uint64_t frac = val & 0xfffffffffffffull;
114 
115     if (exp == 0) {
116         /* Denormals without /S raise an exception.  */
117         if (frac != 0) {
118             arith_excp(env, GETPC(), EXC_M_INV, 0);
119         }
120     } else if (exp == 0x7ff) {
121         /* Infinity or NaN.  */
122         env->fpcr |= FPCR_INV;
123         arith_excp(env, GETPC(), EXC_M_INV, 0);
124     }
125 }
126 
127 /* Similar, but does not trap for infinities.  Used for comparisons.  */
helper_ieee_input_cmp(CPUAlphaState * env,uint64_t val)128 void helper_ieee_input_cmp(CPUAlphaState *env, uint64_t val)
129 {
130     uint32_t exp = (uint32_t)(val >> 52) & 0x7ff;
131     uint64_t frac = val & 0xfffffffffffffull;
132 
133     if (exp == 0) {
134         /* Denormals without /S raise an exception.  */
135         if (frac != 0) {
136             arith_excp(env, GETPC(), EXC_M_INV, 0);
137         }
138     } else if (exp == 0x7ff && frac) {
139         /* NaN.  */
140         env->fpcr |= FPCR_INV;
141         arith_excp(env, GETPC(), EXC_M_INV, 0);
142     }
143 }
144 
145 /* Input handing with software completion.  Trap for denorms, unless DNZ
146    is set.  If we try to support DNOD (which none of the produced hardware
147    did, AFAICS), we'll need to suppress the trap when FPCR.DNOD is set;
148    then the code downstream of that will need to cope with denorms sans
149    flush_input_to_zero.  Most of it should work sanely, but there's
150    nothing to compare with.  */
helper_ieee_input_s(CPUAlphaState * env,uint64_t val)151 void helper_ieee_input_s(CPUAlphaState *env, uint64_t val)
152 {
153     if (unlikely(2 * val - 1 < 0x1fffffffffffffull)
154         && !env->fp_status.flush_inputs_to_zero) {
155         arith_excp(env, GETPC(), EXC_M_INV | EXC_M_SWC, 0);
156     }
157 }
158 
159 /* S floating (single) */
160 
161 /* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg.  */
float32_to_s_int(uint32_t fi)162 static inline uint64_t float32_to_s_int(uint32_t fi)
163 {
164     uint32_t frac = fi & 0x7fffff;
165     uint32_t sign = fi >> 31;
166     uint32_t exp_msb = (fi >> 30) & 1;
167     uint32_t exp_low = (fi >> 23) & 0x7f;
168     uint32_t exp;
169 
170     exp = (exp_msb << 10) | exp_low;
171     if (exp_msb) {
172         if (exp_low == 0x7f) {
173             exp = 0x7ff;
174         }
175     } else {
176         if (exp_low != 0x00) {
177             exp |= 0x380;
178         }
179     }
180 
181     return (((uint64_t)sign << 63)
182             | ((uint64_t)exp << 52)
183             | ((uint64_t)frac << 29));
184 }
185 
float32_to_s(float32 fa)186 static inline uint64_t float32_to_s(float32 fa)
187 {
188     CPU_FloatU a;
189     a.f = fa;
190     return float32_to_s_int(a.l);
191 }
192 
s_to_float32_int(uint64_t a)193 static inline uint32_t s_to_float32_int(uint64_t a)
194 {
195     return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff);
196 }
197 
s_to_float32(uint64_t a)198 static inline float32 s_to_float32(uint64_t a)
199 {
200     CPU_FloatU r;
201     r.l = s_to_float32_int(a);
202     return r.f;
203 }
204 
helper_s_to_memory(uint64_t a)205 uint32_t helper_s_to_memory(uint64_t a)
206 {
207     return s_to_float32_int(a);
208 }
209 
helper_memory_to_s(uint32_t a)210 uint64_t helper_memory_to_s(uint32_t a)
211 {
212     return float32_to_s_int(a);
213 }
214 
helper_adds(CPUAlphaState * env,uint64_t a,uint64_t b)215 uint64_t helper_adds(CPUAlphaState *env, uint64_t a, uint64_t b)
216 {
217     float32 fa, fb, fr;
218 
219     fa = s_to_float32(a);
220     fb = s_to_float32(b);
221     fr = float32_add(fa, fb, &FP_STATUS);
222     env->error_code = soft_to_fpcr_exc(env);
223 
224     return float32_to_s(fr);
225 }
226 
helper_subs(CPUAlphaState * env,uint64_t a,uint64_t b)227 uint64_t helper_subs(CPUAlphaState *env, uint64_t a, uint64_t b)
228 {
229     float32 fa, fb, fr;
230 
231     fa = s_to_float32(a);
232     fb = s_to_float32(b);
233     fr = float32_sub(fa, fb, &FP_STATUS);
234     env->error_code = soft_to_fpcr_exc(env);
235 
236     return float32_to_s(fr);
237 }
238 
helper_muls(CPUAlphaState * env,uint64_t a,uint64_t b)239 uint64_t helper_muls(CPUAlphaState *env, uint64_t a, uint64_t b)
240 {
241     float32 fa, fb, fr;
242 
243     fa = s_to_float32(a);
244     fb = s_to_float32(b);
245     fr = float32_mul(fa, fb, &FP_STATUS);
246     env->error_code = soft_to_fpcr_exc(env);
247 
248     return float32_to_s(fr);
249 }
250 
helper_divs(CPUAlphaState * env,uint64_t a,uint64_t b)251 uint64_t helper_divs(CPUAlphaState *env, uint64_t a, uint64_t b)
252 {
253     float32 fa, fb, fr;
254 
255     fa = s_to_float32(a);
256     fb = s_to_float32(b);
257     fr = float32_div(fa, fb, &FP_STATUS);
258     env->error_code = soft_to_fpcr_exc(env);
259 
260     return float32_to_s(fr);
261 }
262 
helper_sqrts(CPUAlphaState * env,uint64_t a)263 uint64_t helper_sqrts(CPUAlphaState *env, uint64_t a)
264 {
265     float32 fa, fr;
266 
267     fa = s_to_float32(a);
268     fr = float32_sqrt(fa, &FP_STATUS);
269     env->error_code = soft_to_fpcr_exc(env);
270 
271     return float32_to_s(fr);
272 }
273 
274 
275 /* T floating (double) */
t_to_float64(uint64_t a)276 static inline float64 t_to_float64(uint64_t a)
277 {
278     /* Memory format is the same as float64 */
279     CPU_DoubleU r;
280     r.ll = a;
281     return r.d;
282 }
283 
float64_to_t(float64 fa)284 static inline uint64_t float64_to_t(float64 fa)
285 {
286     /* Memory format is the same as float64 */
287     CPU_DoubleU r;
288     r.d = fa;
289     return r.ll;
290 }
291 
helper_addt(CPUAlphaState * env,uint64_t a,uint64_t b)292 uint64_t helper_addt(CPUAlphaState *env, uint64_t a, uint64_t b)
293 {
294     float64 fa, fb, fr;
295 
296     fa = t_to_float64(a);
297     fb = t_to_float64(b);
298     fr = float64_add(fa, fb, &FP_STATUS);
299     env->error_code = soft_to_fpcr_exc(env);
300 
301     return float64_to_t(fr);
302 }
303 
helper_subt(CPUAlphaState * env,uint64_t a,uint64_t b)304 uint64_t helper_subt(CPUAlphaState *env, uint64_t a, uint64_t b)
305 {
306     float64 fa, fb, fr;
307 
308     fa = t_to_float64(a);
309     fb = t_to_float64(b);
310     fr = float64_sub(fa, fb, &FP_STATUS);
311     env->error_code = soft_to_fpcr_exc(env);
312 
313     return float64_to_t(fr);
314 }
315 
helper_mult(CPUAlphaState * env,uint64_t a,uint64_t b)316 uint64_t helper_mult(CPUAlphaState *env, uint64_t a, uint64_t b)
317 {
318     float64 fa, fb, fr;
319 
320     fa = t_to_float64(a);
321     fb = t_to_float64(b);
322     fr = float64_mul(fa, fb, &FP_STATUS);
323     env->error_code = soft_to_fpcr_exc(env);
324 
325     return float64_to_t(fr);
326 }
327 
helper_divt(CPUAlphaState * env,uint64_t a,uint64_t b)328 uint64_t helper_divt(CPUAlphaState *env, uint64_t a, uint64_t b)
329 {
330     float64 fa, fb, fr;
331 
332     fa = t_to_float64(a);
333     fb = t_to_float64(b);
334     fr = float64_div(fa, fb, &FP_STATUS);
335     env->error_code = soft_to_fpcr_exc(env);
336 
337     return float64_to_t(fr);
338 }
339 
helper_sqrtt(CPUAlphaState * env,uint64_t a)340 uint64_t helper_sqrtt(CPUAlphaState *env, uint64_t a)
341 {
342     float64 fa, fr;
343 
344     fa = t_to_float64(a);
345     fr = float64_sqrt(fa, &FP_STATUS);
346     env->error_code = soft_to_fpcr_exc(env);
347 
348     return float64_to_t(fr);
349 }
350 
351 /* Comparisons */
helper_cmptun(CPUAlphaState * env,uint64_t a,uint64_t b)352 uint64_t helper_cmptun(CPUAlphaState *env, uint64_t a, uint64_t b)
353 {
354     float64 fa, fb;
355     uint64_t ret = 0;
356 
357     fa = t_to_float64(a);
358     fb = t_to_float64(b);
359 
360     if (float64_unordered_quiet(fa, fb, &FP_STATUS)) {
361         ret = 0x4000000000000000ULL;
362     }
363     env->error_code = soft_to_fpcr_exc(env);
364 
365     return ret;
366 }
367 
helper_cmpteq(CPUAlphaState * env,uint64_t a,uint64_t b)368 uint64_t helper_cmpteq(CPUAlphaState *env, uint64_t a, uint64_t b)
369 {
370     float64 fa, fb;
371     uint64_t ret = 0;
372 
373     fa = t_to_float64(a);
374     fb = t_to_float64(b);
375 
376     if (float64_eq_quiet(fa, fb, &FP_STATUS)) {
377         ret = 0x4000000000000000ULL;
378     }
379     env->error_code = soft_to_fpcr_exc(env);
380 
381     return ret;
382 }
383 
helper_cmptle(CPUAlphaState * env,uint64_t a,uint64_t b)384 uint64_t helper_cmptle(CPUAlphaState *env, uint64_t a, uint64_t b)
385 {
386     float64 fa, fb;
387     uint64_t ret = 0;
388 
389     fa = t_to_float64(a);
390     fb = t_to_float64(b);
391 
392     if (float64_le(fa, fb, &FP_STATUS)) {
393         ret = 0x4000000000000000ULL;
394     }
395     env->error_code = soft_to_fpcr_exc(env);
396 
397     return ret;
398 }
399 
helper_cmptlt(CPUAlphaState * env,uint64_t a,uint64_t b)400 uint64_t helper_cmptlt(CPUAlphaState *env, uint64_t a, uint64_t b)
401 {
402     float64 fa, fb;
403     uint64_t ret = 0;
404 
405     fa = t_to_float64(a);
406     fb = t_to_float64(b);
407 
408     if (float64_lt(fa, fb, &FP_STATUS)) {
409         ret = 0x4000000000000000ULL;
410     }
411     env->error_code = soft_to_fpcr_exc(env);
412 
413     return ret;
414 }
415 
416 /* Floating point format conversion */
helper_cvtts(CPUAlphaState * env,uint64_t a)417 uint64_t helper_cvtts(CPUAlphaState *env, uint64_t a)
418 {
419     float64 fa;
420     float32 fr;
421 
422     fa = t_to_float64(a);
423     fr = float64_to_float32(fa, &FP_STATUS);
424     env->error_code = soft_to_fpcr_exc(env);
425 
426     return float32_to_s(fr);
427 }
428 
helper_cvtst(CPUAlphaState * env,uint64_t a)429 uint64_t helper_cvtst(CPUAlphaState *env, uint64_t a)
430 {
431     float32 fa;
432     float64 fr;
433 
434     fa = s_to_float32(a);
435     fr = float32_to_float64(fa, &FP_STATUS);
436     env->error_code = soft_to_fpcr_exc(env);
437 
438     return float64_to_t(fr);
439 }
440 
helper_cvtqs(CPUAlphaState * env,uint64_t a)441 uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a)
442 {
443     float32 fr = int64_to_float32(a, &FP_STATUS);
444     env->error_code = soft_to_fpcr_exc(env);
445 
446     return float32_to_s(fr);
447 }
448 
449 /* Implement float64 to uint64_t conversion without saturation -- we must
450    supply the truncated result.  This behaviour is used by the compiler
451    to get unsigned conversion for free with the same instruction.  */
452 
do_cvttq(CPUAlphaState * env,uint64_t a,int roundmode)453 static uint64_t do_cvttq(CPUAlphaState *env, uint64_t a, int roundmode)
454 {
455     float64 fa;
456     int64_t ret;
457     uint32_t exc = 0;
458     int flags;
459 
460     fa = t_to_float64(a);
461     ret = float64_to_int64_modulo(fa, roundmode, &FP_STATUS);
462 
463     flags = get_float_exception_flags(&FP_STATUS);
464     if (unlikely(flags)) {
465         set_float_exception_flags(0, &FP_STATUS);
466 
467         /* We need to massage the resulting exceptions. */
468         if (flags & 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 (flags & float_flag_invalid) {
476             exc = FPCR_INV;
477         } else if (flags & float_flag_inexact) {
478             exc = FPCR_INE;
479         }
480     }
481     env->error_code = exc;
482 
483     return ret;
484 }
485 
helper_cvttq(CPUAlphaState * env,uint64_t a)486 uint64_t helper_cvttq(CPUAlphaState *env, uint64_t a)
487 {
488     return do_cvttq(env, a, FP_STATUS.float_rounding_mode);
489 }
490 
helper_cvttq_c(CPUAlphaState * env,uint64_t a)491 uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a)
492 {
493     return do_cvttq(env, a, float_round_to_zero);
494 }
495 
helper_cvtqt(CPUAlphaState * env,uint64_t a)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 
helper_cvtql(CPUAlphaState * env,uint64_t val)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