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