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