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