master
c 1,163 lines 37.1 KB
Raw
1 /*
2 * S/390 FPU helper routines
3 *
4 * Copyright (c) 2009 Ulrich Hecht
5 * Copyright (c) 2009 Alexander Graf
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "qemu/osdep.h"
22 #include "cpu.h"
23 #include "s390x-internal.h"
24 #include "tcg_s390x.h"
25 #include "exec/helper-proto.h"
26 #include "fpu/softfloat.h"
27 #include "fpu/softfloat-parts.h"
28
29 /* #define DEBUG_HELPER */
30 #ifdef DEBUG_HELPER
31 #define HELPER_LOG(x...) qemu_log(x)
32 #else
33 #define HELPER_LOG(x...)
34 #endif
35
36 static inline Int128 RET128(float128 f)
37 {
38 return int128_make128(f.low, f.high);
39 }
40
41 static inline float128 ARG128(Int128 i)
42 {
43 return make_float128(int128_gethi(i), int128_getlo(i));
44 }
45
46 uint8_t s390_softfloat_exc_to_ieee(unsigned int exc)
47 {
48 uint8_t s390_exc = 0;
49
50 s390_exc |= (exc & float_flag_invalid) ? S390_IEEE_MASK_INVALID : 0;
51 s390_exc |= (exc & float_flag_divbyzero) ? S390_IEEE_MASK_DIVBYZERO : 0;
52 s390_exc |= (exc & float_flag_overflow) ? S390_IEEE_MASK_OVERFLOW : 0;
53 s390_exc |= (exc & float_flag_underflow) ? S390_IEEE_MASK_UNDERFLOW : 0;
54 s390_exc |= (exc & (float_flag_inexact | float_flag_invalid_cvti)) ?
55 S390_IEEE_MASK_INEXACT : 0;
56
57 return s390_exc;
58 }
59
60 static int s390_get_bfp_rounding_mode(CPUS390XState *env, int m3)
61 {
62 switch (m3) {
63 case 0:
64 /* current mode */
65 return get_float_rounding_mode(&env->fpu_status);
66 case 1:
67 /* round to nearest with ties away from 0 */
68 return float_round_ties_away;
69 case 3:
70 /* round to prepare for shorter precision */
71 return float_round_to_odd;
72 case 4:
73 /* round to nearest with ties to even */
74 return float_round_nearest_even;
75 case 5:
76 /* round to zero */
77 return float_round_to_zero;
78 case 6:
79 /* round to +inf */
80 return float_round_up;
81 case 7:
82 /* round to -inf */
83 return float_round_down;
84 default:
85 g_assert_not_reached();
86 }
87 }
88
89 /* Should be called after any operation that may raise IEEE exceptions. */
90 static void handle_exceptions(CPUS390XState *env, bool XxC, uintptr_t retaddr)
91 {
92 unsigned s390_exc, qemu_exc;
93
94 /* Get the exceptions raised by the current operation. Reset the
95 fpu_status contents so that the next operation has a clean slate. */
96 qemu_exc = env->fpu_status.float_exception_flags;
97 if (qemu_exc == 0) {
98 return;
99 }
100 env->fpu_status.float_exception_flags = 0;
101 s390_exc = s390_softfloat_exc_to_ieee(qemu_exc);
102
103 /*
104 * IEEE-Underflow exception recognition exists if a tininess condition
105 * (underflow) exists and
106 * - The mask bit in the FPC is zero and the result is inexact
107 * - The mask bit in the FPC is one
108 * So tininess conditions that are not inexact don't trigger any
109 * underflow action in case the mask bit is not one.
110 */
111 if (!(s390_exc & S390_IEEE_MASK_INEXACT) &&
112 !((env->fpc >> 24) & S390_IEEE_MASK_UNDERFLOW)) {
113 s390_exc &= ~S390_IEEE_MASK_UNDERFLOW;
114 }
115
116 /*
117 * FIXME:
118 * 1. Right now, all inexact conditions are indicated as
119 * "truncated" (0) and never as "incremented" (1) in the DXC.
120 * 2. Only traps due to invalid/divbyzero are suppressing. Other traps
121 * are completing, meaning the target register has to be written!
122 * This, however will mean that we have to write the register before
123 * triggering the trap - impossible right now.
124 */
125
126 /*
127 * invalid/divbyzero cannot coexist with other conditions.
128 * overflow/underflow however can coexist with inexact, we have to
129 * handle it separately.
130 */
131 if (s390_exc & ~S390_IEEE_MASK_INEXACT) {
132 if (s390_exc & ~S390_IEEE_MASK_INEXACT & env->fpc >> 24) {
133 /* trap condition - inexact reported along */
134 tcg_s390_data_exception(env, s390_exc, retaddr);
135 }
136 /* nontrap condition - inexact handled differently */
137 env->fpc |= (s390_exc & ~S390_IEEE_MASK_INEXACT) << 16;
138 }
139
140 /* inexact handling */
141 if (s390_exc & S390_IEEE_MASK_INEXACT && !XxC) {
142 /* trap condition - overflow/underflow _not_ reported along */
143 if (s390_exc & S390_IEEE_MASK_INEXACT & env->fpc >> 24) {
144 tcg_s390_data_exception(env, s390_exc & S390_IEEE_MASK_INEXACT,
145 retaddr);
146 }
147 /* nontrap condition */
148 env->fpc |= (s390_exc & S390_IEEE_MASK_INEXACT) << 16;
149 }
150 }
151
152 int float_comp_to_cc(CPUS390XState *env, FloatRelation float_compare)
153 {
154 switch (float_compare) {
155 case float_relation_equal:
156 return 0;
157 case float_relation_less:
158 return 1;
159 case float_relation_greater:
160 return 2;
161 case float_relation_unordered:
162 return 3;
163 default:
164 cpu_abort(env_cpu(env), "unknown return value for float compare\n");
165 }
166 }
167
168 /* condition codes for unary FP ops */
169 uint32_t set_cc_nz_f32(float32 v)
170 {
171 if (float32_is_any_nan(v)) {
172 return 3;
173 } else if (float32_is_zero(v)) {
174 return 0;
175 } else if (float32_is_neg(v)) {
176 return 1;
177 } else {
178 return 2;
179 }
180 }
181
182 uint32_t set_cc_nz_f64(float64 v)
183 {
184 if (float64_is_any_nan(v)) {
185 return 3;
186 } else if (float64_is_zero(v)) {
187 return 0;
188 } else if (float64_is_neg(v)) {
189 return 1;
190 } else {
191 return 2;
192 }
193 }
194
195 uint32_t set_cc_nz_f128(float128 v)
196 {
197 if (float128_is_any_nan(v)) {
198 return 3;
199 } else if (float128_is_zero(v)) {
200 return 0;
201 } else if (float128_is_neg(v)) {
202 return 1;
203 } else {
204 return 2;
205 }
206 }
207
208 /* condition codes for FP to integer conversion ops */
209 static uint32_t set_cc_conv_f32(float32 v, float_status *stat)
210 {
211 if (stat->float_exception_flags & float_flag_invalid) {
212 return 3;
213 } else {
214 return set_cc_nz_f32(v);
215 }
216 }
217
218 static uint32_t set_cc_conv_f64(float64 v, float_status *stat)
219 {
220 if (stat->float_exception_flags & float_flag_invalid) {
221 return 3;
222 } else {
223 return set_cc_nz_f64(v);
224 }
225 }
226
227 static uint32_t set_cc_conv_f128(float128 v, float_status *stat)
228 {
229 if (stat->float_exception_flags & float_flag_invalid) {
230 return 3;
231 } else {
232 return set_cc_nz_f128(v);
233 }
234 }
235
236 static inline uint8_t round_from_m34(uint32_t m34)
237 {
238 return extract32(m34, 0, 4);
239 }
240
241 static inline bool xxc_from_m34(uint32_t m34)
242 {
243 /* XxC is bit 1 of m4 */
244 return extract32(m34, 4 + 3 - 1, 1);
245 }
246
247 /* 32-bit FP addition */
248 uint64_t HELPER(aeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
249 {
250 float32 ret = float32_add(f1, f2, &env->fpu_status);
251 handle_exceptions(env, false, GETPC());
252 return ret;
253 }
254
255 /* 64-bit FP addition */
256 uint64_t HELPER(adb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
257 {
258 float64 ret = float64_add(f1, f2, &env->fpu_status);
259 handle_exceptions(env, false, GETPC());
260 return ret;
261 }
262
263 /* 128-bit FP addition */
264 Int128 HELPER(axb)(CPUS390XState *env, Int128 a, Int128 b)
265 {
266 float128 ret = float128_add(ARG128(a), ARG128(b), &env->fpu_status);
267 handle_exceptions(env, false, GETPC());
268 return RET128(ret);
269 }
270
271 /* 32-bit FP subtraction */
272 uint64_t HELPER(seb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
273 {
274 float32 ret = float32_sub(f1, f2, &env->fpu_status);
275 handle_exceptions(env, false, GETPC());
276 return ret;
277 }
278
279 /* 64-bit FP subtraction */
280 uint64_t HELPER(sdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
281 {
282 float64 ret = float64_sub(f1, f2, &env->fpu_status);
283 handle_exceptions(env, false, GETPC());
284 return ret;
285 }
286
287 /* 128-bit FP subtraction */
288 Int128 HELPER(sxb)(CPUS390XState *env, Int128 a, Int128 b)
289 {
290 float128 ret = float128_sub(ARG128(a), ARG128(b), &env->fpu_status);
291 handle_exceptions(env, false, GETPC());
292 return RET128(ret);
293 }
294
295 /* 32-bit FP division */
296 uint64_t HELPER(deb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
297 {
298 float32 ret = float32_div(f1, f2, &env->fpu_status);
299 handle_exceptions(env, false, GETPC());
300 return ret;
301 }
302
303 /* 64-bit FP division */
304 uint64_t HELPER(ddb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
305 {
306 float64 ret = float64_div(f1, f2, &env->fpu_status);
307 handle_exceptions(env, false, GETPC());
308 return ret;
309 }
310
311 /* 128-bit FP division */
312 Int128 HELPER(dxb)(CPUS390XState *env, Int128 a, Int128 b)
313 {
314 float128 ret = float128_div(ARG128(a), ARG128(b), &env->fpu_status);
315 handle_exceptions(env, false, GETPC());
316 return RET128(ret);
317 }
318
319 static void parts_s390_divide_to_integer(FloatParts64 *a, FloatParts64 *b,
320 int final_quotient_rounding_mode,
321 bool mask_underflow, bool mask_inexact,
322 const FloatFmt *fmt,
323 FloatParts64 *r, FloatParts64 *n,
324 uint32_t *cc, int *dxc,
325 float_status *status)
326 {
327 /* POp table "Results: DIVIDE TO INTEGER (Part 1 of 2)" */
328 if ((float_cmask(a->cls) | float_cmask(b->cls)) & float_cmask_anynan) {
329 *r = parts64_pick_nan(a, b, status);
330 *n = *r;
331 *cc = 1;
332 } else if (a->cls == float_class_inf || b->cls == float_class_zero) {
333 *r = parts64_default_nan(status);
334 *n = *r;
335 *cc = 1;
336 status->float_exception_flags |= float_flag_invalid;
337 } else if (b->cls == float_class_inf) {
338 *r = *a;
339 n->cls = float_class_zero;
340 n->sign = a->sign ^ b->sign;
341 *cc = 0;
342 } else {
343 FloatParts64 *q, q_buf, r_precise;
344 int float_exception_flags = 0;
345 bool is_q_smallish;
346 uint32_t r_flags;
347
348 /* Compute precise quotient */
349 q_buf = parts64_div(a, b, status);
350 q = &q_buf;
351
352 /*
353 * Check whether two closest integers can be precisely represented,
354 * i.e., all their bits fit into the fractional part.
355 */
356 is_q_smallish = q->exp < (fmt->frac_size + 1);
357
358 /*
359 * Final quotient is rounded using final-quotient-rounding method, and
360 * partial quotient is rounded toward zero.
361 *
362 * Rounding of partial quotient may be inexact. This is the whole point
363 * of distinguishing partial quotients, so ignore the exception.
364 */
365 *n = parts64_round_to_int(q,
366 is_q_smallish
367 ? final_quotient_rounding_mode
368 : float_round_to_zero,
369 0, status, fmt);
370
371 /* Compute precise remainder */
372 r_precise = parts64_muladd(b, n, a,
373 float_muladd_negate_product, status);
374
375 /* Round remainder to the target format */
376 *r = r_precise;
377 status->float_exception_flags = 0;
378 *r = parts64_round_to_fmt(r, status, fmt);
379 r_flags = status->float_exception_flags;
380
381 /* POp table "Results: DIVIDE TO INTEGER (Part 2 of 2)" */
382 if (is_q_smallish) {
383 if (r->cls != float_class_zero) {
384 if (r->exp < 2 - (1 << (fmt->exp_size - 1))) {
385 if (mask_underflow) {
386 float_exception_flags |= float_flag_underflow;
387 *dxc = 0x10;
388 r->exp += fmt->exp_re_bias;
389 }
390 } else if (r_flags & float_flag_inexact) {
391 float_exception_flags |= float_flag_inexact;
392 if (mask_inexact) {
393 bool saved_r_sign, saved_r_precise_sign;
394
395 /*
396 * Check whether remainder was truncated (rounded
397 * toward zero) or incremented.
398 */
399 saved_r_sign = r->sign;
400 saved_r_precise_sign = r_precise.sign;
401 r->sign = false;
402 r_precise.sign = false;
403 if (parts64_compare(r, &r_precise, status, true) <
404 float_relation_equal) {
405 *dxc = 0x8;
406 } else {
407 *dxc = 0xc;
408 }
409 r->sign = saved_r_sign;
410 r_precise.sign = saved_r_precise_sign;
411 }
412 }
413 }
414 *cc = 0;
415 } else if (n->exp > (1 << (fmt->exp_size - 1)) - 1) {
416 n->exp -= fmt->exp_re_bias;
417 *cc = r->cls == float_class_zero ? 1 : 3;
418 } else {
419 *cc = r->cls == float_class_zero ? 0 : 2;
420 }
421
422 /* Adjust signs of zero results */
423 if (r->cls == float_class_zero) {
424 r->sign = a->sign;
425 }
426 if (n->cls == float_class_zero) {
427 n->sign = a->sign ^ b->sign;
428 }
429
430 status->float_exception_flags = float_exception_flags;
431 }
432 }
433
434 #define DEFINE_S390_DIVIDE_TO_INTEGER(floatN) \
435 static void floatN ## _s390_divide_to_integer(floatN a, floatN b, \
436 int final_quotient_rounding_mode, bool mask_underflow, bool mask_inexact, \
437 floatN *r, floatN *n, uint32_t *cc, int *dxc, float_status *status) \
438 { \
439 FloatParts64 pa = floatN ## _unpack_canonical(a, status); \
440 FloatParts64 pb = floatN ## _unpack_canonical(b, status); \
441 FloatParts64 pr, pn; \
442 parts_s390_divide_to_integer(&pa, &pb, final_quotient_rounding_mode, \
443 mask_underflow, mask_inexact, \
444 &floatN ## _params, \
445 &pr, &pn, cc, dxc, status); \
446 *r = floatN ## _round_pack_canonical(&pr, status); \
447 *n = floatN ## _round_pack_canonical(&pn, status); \
448 }
449
450 DEFINE_S390_DIVIDE_TO_INTEGER(float32)
451 DEFINE_S390_DIVIDE_TO_INTEGER(float64)
452
453 void HELPER(dib)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3,
454 uint32_t m4, uint32_t bits)
455 {
456 int final_quotient_rounding_mode = s390_get_bfp_rounding_mode(env, m4);
457 bool mask_underflow = (env->fpc >> 24) & S390_IEEE_MASK_UNDERFLOW;
458 bool mask_inexact = (env->fpc >> 24) & S390_IEEE_MASK_INEXACT;
459 float32 a32, b32, n32, r32;
460 float64 a64, b64, n64, r64;
461 int dxc = -1;
462 uint32_t cc;
463
464 if (bits == 32) {
465 a32 = env->vregs[r1][0] >> 32;
466 b32 = env->vregs[r2][0] >> 32;
467
468 float32_s390_divide_to_integer(
469 a32, b32,
470 final_quotient_rounding_mode,
471 mask_underflow, mask_inexact,
472 &r32, &n32, &cc, &dxc, &env->fpu_status);
473 } else {
474 a64 = env->vregs[r1][0];
475 b64 = env->vregs[r2][0];
476
477 float64_s390_divide_to_integer(
478 a64, b64,
479 final_quotient_rounding_mode,
480 mask_underflow, mask_inexact,
481 &r64, &n64, &cc, &dxc, &env->fpu_status);
482 }
483
484 /* Flush the results if needed */
485 if ((env->fpu_status.float_exception_flags & float_flag_invalid) &&
486 ((env->fpc >> 24) & S390_IEEE_MASK_INVALID)) {
487 /* The action for invalid operation is "Suppress" */
488 } else {
489 /* The action for other exceptions is "Complete" */
490 if (bits == 32) {
491 env->vregs[r1][0] = deposit64(env->vregs[r1][0], 32, 32, r32);
492 env->vregs[r3][0] = deposit64(env->vregs[r3][0], 32, 32, n32);
493 } else {
494 env->vregs[r1][0] = r64;
495 env->vregs[r3][0] = n64;
496 }
497 env->cc_op = cc;
498 }
499
500 /* Raise an exception if needed */
501 if (dxc == -1) {
502 handle_exceptions(env, false, GETPC());
503 } else {
504 env->fpu_status.float_exception_flags = 0;
505 tcg_s390_data_exception(env, dxc, GETPC());
506 }
507 }
508
509 /* 32-bit FP multiplication */
510 uint64_t HELPER(meeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
511 {
512 float32 ret = float32_mul(f1, f2, &env->fpu_status);
513 handle_exceptions(env, false, GETPC());
514 return ret;
515 }
516
517 /* 64-bit FP multiplication */
518 uint64_t HELPER(mdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
519 {
520 float64 ret = float64_mul(f1, f2, &env->fpu_status);
521 handle_exceptions(env, false, GETPC());
522 return ret;
523 }
524
525 /* 64/32-bit FP multiplication */
526 uint64_t HELPER(mdeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
527 {
528 float64 f1_64 = float32_to_float64(f1, &env->fpu_status);
529 float64 ret = float32_to_float64(f2, &env->fpu_status);
530 ret = float64_mul(f1_64, ret, &env->fpu_status);
531 handle_exceptions(env, false, GETPC());
532 return ret;
533 }
534
535 /* 128-bit FP multiplication */
536 Int128 HELPER(mxb)(CPUS390XState *env, Int128 a, Int128 b)
537 {
538 float128 ret = float128_mul(ARG128(a), ARG128(b), &env->fpu_status);
539 handle_exceptions(env, false, GETPC());
540 return RET128(ret);
541 }
542
543 /* 128/64-bit FP multiplication */
544 Int128 HELPER(mxdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
545 {
546 float128 f1_128 = float64_to_float128(f1, &env->fpu_status);
547 float128 ret = float64_to_float128(f2, &env->fpu_status);
548 ret = float128_mul(f1_128, ret, &env->fpu_status);
549 handle_exceptions(env, false, GETPC());
550 return RET128(ret);
551 }
552
553 /* convert 32-bit float to 64-bit float */
554 uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2)
555 {
556 float64 ret = float32_to_float64(f2, &env->fpu_status);
557 handle_exceptions(env, false, GETPC());
558 return ret;
559 }
560
561 /* convert 128-bit float to 64-bit float */
562 uint64_t HELPER(ldxb)(CPUS390XState *env, Int128 a, uint32_t m34)
563 {
564 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
565 float64 ret = float128_to_float64(ARG128(a), &env->fpu_status);
566
567 s390_restore_bfp_rounding_mode(env, old_mode);
568 handle_exceptions(env, xxc_from_m34(m34), GETPC());
569 return ret;
570 }
571
572 /* convert 64-bit float to 128-bit float */
573 Int128 HELPER(lxdb)(CPUS390XState *env, uint64_t f2)
574 {
575 float128 ret = float64_to_float128(f2, &env->fpu_status);
576 handle_exceptions(env, false, GETPC());
577 return RET128(ret);
578 }
579
580 /* convert 32-bit float to 128-bit float */
581 Int128 HELPER(lxeb)(CPUS390XState *env, uint64_t f2)
582 {
583 float128 ret = float32_to_float128(f2, &env->fpu_status);
584 handle_exceptions(env, false, GETPC());
585 return RET128(ret);
586 }
587
588 /* convert 64-bit float to 32-bit float */
589 uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2, uint32_t m34)
590 {
591 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
592 float32 ret = float64_to_float32(f2, &env->fpu_status);
593
594 s390_restore_bfp_rounding_mode(env, old_mode);
595 handle_exceptions(env, xxc_from_m34(m34), GETPC());
596 return ret;
597 }
598
599 /* convert 128-bit float to 32-bit float */
600 uint64_t HELPER(lexb)(CPUS390XState *env, Int128 a, uint32_t m34)
601 {
602 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
603 float32 ret = float128_to_float32(ARG128(a), &env->fpu_status);
604
605 s390_restore_bfp_rounding_mode(env, old_mode);
606 handle_exceptions(env, xxc_from_m34(m34), GETPC());
607 return ret;
608 }
609
610 /* 32-bit FP compare */
611 uint32_t HELPER(ceb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
612 {
613 FloatRelation cmp = float32_compare_quiet(f1, f2, &env->fpu_status);
614 handle_exceptions(env, false, GETPC());
615 return float_comp_to_cc(env, cmp);
616 }
617
618 /* 64-bit FP compare */
619 uint32_t HELPER(cdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
620 {
621 FloatRelation cmp = float64_compare_quiet(f1, f2, &env->fpu_status);
622 handle_exceptions(env, false, GETPC());
623 return float_comp_to_cc(env, cmp);
624 }
625
626 /* 128-bit FP compare */
627 uint32_t HELPER(cxb)(CPUS390XState *env, Int128 a, Int128 b)
628 {
629 FloatRelation cmp = float128_compare_quiet(ARG128(a), ARG128(b),
630 &env->fpu_status);
631 handle_exceptions(env, false, GETPC());
632 return float_comp_to_cc(env, cmp);
633 }
634
635 int s390_swap_bfp_rounding_mode(CPUS390XState *env, int m3)
636 {
637 int ret = get_float_rounding_mode(&env->fpu_status);
638
639 set_float_rounding_mode(s390_get_bfp_rounding_mode(env, m3),
640 &env->fpu_status);
641 return ret;
642 }
643
644 void s390_restore_bfp_rounding_mode(CPUS390XState *env, int old_mode)
645 {
646 set_float_rounding_mode(old_mode, &env->fpu_status);
647 }
648
649 /* convert 64-bit int to 32-bit float */
650 uint64_t HELPER(cegb)(CPUS390XState *env, int64_t v2, uint32_t m34)
651 {
652 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
653 float32 ret = int64_to_float32(v2, &env->fpu_status);
654
655 s390_restore_bfp_rounding_mode(env, old_mode);
656 handle_exceptions(env, xxc_from_m34(m34), GETPC());
657 return ret;
658 }
659
660 /* convert 64-bit int to 64-bit float */
661 uint64_t HELPER(cdgb)(CPUS390XState *env, int64_t v2, uint32_t m34)
662 {
663 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
664 float64 ret = int64_to_float64(v2, &env->fpu_status);
665
666 s390_restore_bfp_rounding_mode(env, old_mode);
667 handle_exceptions(env, xxc_from_m34(m34), GETPC());
668 return ret;
669 }
670
671 /* convert 64-bit int to 128-bit float */
672 Int128 HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m34)
673 {
674 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
675 float128 ret = int64_to_float128(v2, &env->fpu_status);
676
677 s390_restore_bfp_rounding_mode(env, old_mode);
678 handle_exceptions(env, xxc_from_m34(m34), GETPC());
679 return RET128(ret);
680 }
681
682 /* convert 64-bit uint to 32-bit float */
683 uint64_t HELPER(celgb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
684 {
685 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
686 float32 ret = uint64_to_float32(v2, &env->fpu_status);
687
688 s390_restore_bfp_rounding_mode(env, old_mode);
689 handle_exceptions(env, xxc_from_m34(m34), GETPC());
690 return ret;
691 }
692
693 /* convert 64-bit uint to 64-bit float */
694 uint64_t HELPER(cdlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
695 {
696 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
697 float64 ret = uint64_to_float64(v2, &env->fpu_status);
698
699 s390_restore_bfp_rounding_mode(env, old_mode);
700 handle_exceptions(env, xxc_from_m34(m34), GETPC());
701 return ret;
702 }
703
704 /* convert 64-bit uint to 128-bit float */
705 Int128 HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
706 {
707 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
708 float128 ret = uint64_to_float128(v2, &env->fpu_status);
709
710 s390_restore_bfp_rounding_mode(env, old_mode);
711 handle_exceptions(env, xxc_from_m34(m34), GETPC());
712 return RET128(ret);
713 }
714
715 /* convert 32-bit float to 64-bit int */
716 uint64_t HELPER(cgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
717 {
718 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
719 int64_t ret = float32_to_int64(v2, &env->fpu_status);
720 uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status);
721
722 s390_restore_bfp_rounding_mode(env, old_mode);
723 handle_exceptions(env, xxc_from_m34(m34), GETPC());
724 env->cc_op = cc;
725 if (float32_is_any_nan(v2)) {
726 return INT64_MIN;
727 }
728 return ret;
729 }
730
731 /* convert 64-bit float to 64-bit int */
732 uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
733 {
734 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
735 int64_t ret = float64_to_int64(v2, &env->fpu_status);
736 uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status);
737
738 s390_restore_bfp_rounding_mode(env, old_mode);
739 handle_exceptions(env, xxc_from_m34(m34), GETPC());
740 env->cc_op = cc;
741 if (float64_is_any_nan(v2)) {
742 return INT64_MIN;
743 }
744 return ret;
745 }
746
747 /* convert 128-bit float to 64-bit int */
748 uint64_t HELPER(cgxb)(CPUS390XState *env, Int128 i2, uint32_t m34)
749 {
750 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
751 float128 v2 = ARG128(i2);
752 int64_t ret = float128_to_int64(v2, &env->fpu_status);
753 uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status);
754
755 s390_restore_bfp_rounding_mode(env, old_mode);
756 handle_exceptions(env, xxc_from_m34(m34), GETPC());
757 env->cc_op = cc;
758 if (float128_is_any_nan(v2)) {
759 return INT64_MIN;
760 }
761 return ret;
762 }
763
764 /* convert 32-bit float to 32-bit int */
765 uint64_t HELPER(cfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
766 {
767 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
768 int32_t ret = float32_to_int32(v2, &env->fpu_status);
769 uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status);
770
771 s390_restore_bfp_rounding_mode(env, old_mode);
772 handle_exceptions(env, xxc_from_m34(m34), GETPC());
773 env->cc_op = cc;
774 if (float32_is_any_nan(v2)) {
775 return INT32_MIN;
776 }
777 return ret;
778 }
779
780 /* convert 64-bit float to 32-bit int */
781 uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
782 {
783 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
784 int32_t ret = float64_to_int32(v2, &env->fpu_status);
785 uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status);
786
787 s390_restore_bfp_rounding_mode(env, old_mode);
788 handle_exceptions(env, xxc_from_m34(m34), GETPC());
789 env->cc_op = cc;
790 if (float64_is_any_nan(v2)) {
791 return INT32_MIN;
792 }
793 return ret;
794 }
795
796 /* convert 128-bit float to 32-bit int */
797 uint64_t HELPER(cfxb)(CPUS390XState *env, Int128 i2, uint32_t m34)
798 {
799 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
800 float128 v2 = ARG128(i2);
801 int32_t ret = float128_to_int32(v2, &env->fpu_status);
802 uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status);
803
804 s390_restore_bfp_rounding_mode(env, old_mode);
805 handle_exceptions(env, xxc_from_m34(m34), GETPC());
806 env->cc_op = cc;
807 if (float128_is_any_nan(v2)) {
808 return INT32_MIN;
809 }
810 return ret;
811 }
812
813 /* convert 32-bit float to 64-bit uint */
814 uint64_t HELPER(clgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
815 {
816 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
817 uint64_t ret = float32_to_uint64(v2, &env->fpu_status);
818 uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status);
819
820 s390_restore_bfp_rounding_mode(env, old_mode);
821 handle_exceptions(env, xxc_from_m34(m34), GETPC());
822 env->cc_op = cc;
823 if (float32_is_any_nan(v2)) {
824 return 0;
825 }
826 return ret;
827 }
828
829 /* convert 64-bit float to 64-bit uint */
830 uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
831 {
832 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
833 uint64_t ret = float64_to_uint64(v2, &env->fpu_status);
834 uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status);
835
836 s390_restore_bfp_rounding_mode(env, old_mode);
837 handle_exceptions(env, xxc_from_m34(m34), GETPC());
838 env->cc_op = cc;
839 if (float64_is_any_nan(v2)) {
840 return 0;
841 }
842 return ret;
843 }
844
845 /* convert 128-bit float to 64-bit uint */
846 uint64_t HELPER(clgxb)(CPUS390XState *env, Int128 i2, uint32_t m34)
847 {
848 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
849 float128 v2 = ARG128(i2);
850 uint64_t ret = float128_to_uint64(v2, &env->fpu_status);
851 uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status);
852
853 s390_restore_bfp_rounding_mode(env, old_mode);
854 handle_exceptions(env, xxc_from_m34(m34), GETPC());
855 env->cc_op = cc;
856 if (float128_is_any_nan(v2)) {
857 return 0;
858 }
859 return ret;
860 }
861
862 /* convert 32-bit float to 32-bit uint */
863 uint64_t HELPER(clfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
864 {
865 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
866 uint32_t ret = float32_to_uint32(v2, &env->fpu_status);
867 uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status);
868
869 s390_restore_bfp_rounding_mode(env, old_mode);
870 handle_exceptions(env, xxc_from_m34(m34), GETPC());
871 env->cc_op = cc;
872 if (float32_is_any_nan(v2)) {
873 return 0;
874 }
875 return ret;
876 }
877
878 /* convert 64-bit float to 32-bit uint */
879 uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
880 {
881 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
882 uint32_t ret = float64_to_uint32(v2, &env->fpu_status);
883 uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status);
884
885 s390_restore_bfp_rounding_mode(env, old_mode);
886 handle_exceptions(env, xxc_from_m34(m34), GETPC());
887 env->cc_op = cc;
888 if (float64_is_any_nan(v2)) {
889 return 0;
890 }
891 return ret;
892 }
893
894 /* convert 128-bit float to 32-bit uint */
895 uint64_t HELPER(clfxb)(CPUS390XState *env, Int128 i2, uint32_t m34)
896 {
897 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
898 float128 v2 = ARG128(i2);
899 uint32_t ret = float128_to_uint32(v2, &env->fpu_status);
900 uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status);
901
902 s390_restore_bfp_rounding_mode(env, old_mode);
903 handle_exceptions(env, xxc_from_m34(m34), GETPC());
904 env->cc_op = cc;
905 if (float128_is_any_nan(v2)) {
906 return 0;
907 }
908 return ret;
909 }
910
911 /* round to integer 32-bit */
912 uint64_t HELPER(fieb)(CPUS390XState *env, uint64_t f2, uint32_t m34)
913 {
914 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
915 float32 ret = float32_round_to_int(f2, &env->fpu_status);
916
917 s390_restore_bfp_rounding_mode(env, old_mode);
918 handle_exceptions(env, xxc_from_m34(m34), GETPC());
919 return ret;
920 }
921
922 /* round to integer 64-bit */
923 uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m34)
924 {
925 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
926 float64 ret = float64_round_to_int(f2, &env->fpu_status);
927
928 s390_restore_bfp_rounding_mode(env, old_mode);
929 handle_exceptions(env, xxc_from_m34(m34), GETPC());
930 return ret;
931 }
932
933 /* round to integer 128-bit */
934 Int128 HELPER(fixb)(CPUS390XState *env, Int128 a, uint32_t m34)
935 {
936 int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
937 float128 ret = float128_round_to_int(ARG128(a), &env->fpu_status);
938
939 s390_restore_bfp_rounding_mode(env, old_mode);
940 handle_exceptions(env, xxc_from_m34(m34), GETPC());
941 return RET128(ret);
942 }
943
944 /* 32-bit FP compare and signal */
945 uint32_t HELPER(keb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
946 {
947 FloatRelation cmp = float32_compare(f1, f2, &env->fpu_status);
948 handle_exceptions(env, false, GETPC());
949 return float_comp_to_cc(env, cmp);
950 }
951
952 /* 64-bit FP compare and signal */
953 uint32_t HELPER(kdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
954 {
955 FloatRelation cmp = float64_compare(f1, f2, &env->fpu_status);
956 handle_exceptions(env, false, GETPC());
957 return float_comp_to_cc(env, cmp);
958 }
959
960 /* 128-bit FP compare and signal */
961 uint32_t HELPER(kxb)(CPUS390XState *env, Int128 a, Int128 b)
962 {
963 FloatRelation cmp = float128_compare(ARG128(a), ARG128(b),
964 &env->fpu_status);
965 handle_exceptions(env, false, GETPC());
966 return float_comp_to_cc(env, cmp);
967 }
968
969 /* 32-bit FP multiply and add */
970 uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1,
971 uint64_t f2, uint64_t f3)
972 {
973 float32 ret = float32_muladd(f3, f2, f1, 0, &env->fpu_status);
974 handle_exceptions(env, false, GETPC());
975 return ret;
976 }
977
978 /* 64-bit FP multiply and add */
979 uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1,
980 uint64_t f2, uint64_t f3)
981 {
982 float64 ret = float64_muladd(f3, f2, f1, 0, &env->fpu_status);
983 handle_exceptions(env, false, GETPC());
984 return ret;
985 }
986
987 /* 32-bit FP multiply and subtract */
988 uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1,
989 uint64_t f2, uint64_t f3)
990 {
991 float32 ret = float32_muladd(f3, f2, f1, float_muladd_negate_c,
992 &env->fpu_status);
993 handle_exceptions(env, false, GETPC());
994 return ret;
995 }
996
997 /* 64-bit FP multiply and subtract */
998 uint64_t HELPER(msdb)(CPUS390XState *env, uint64_t f1,
999 uint64_t f2, uint64_t f3)
1000 {
1001 float64 ret = float64_muladd(f3, f2, f1, float_muladd_negate_c,
1002 &env->fpu_status);
1003 handle_exceptions(env, false, GETPC());
1004 return ret;
1005 }
1006
1007 /* The rightmost bit has the number 11. */
1008 static inline uint16_t dcmask(int bit, bool neg)
1009 {
1010 return 1 << (11 - bit - neg);
1011 }
1012
1013 #define DEF_FLOAT_DCMASK(_TYPE) \
1014 uint16_t _TYPE##_dcmask(CPUS390XState *env, _TYPE f1) \
1015 { \
1016 const bool neg = _TYPE##_is_neg(f1); \
1017 \
1018 /* Sorted by most common cases - only one class is possible */ \
1019 if (_TYPE##_is_normal(f1)) { \
1020 return dcmask(2, neg); \
1021 } else if (_TYPE##_is_zero(f1)) { \
1022 return dcmask(0, neg); \
1023 } else if (_TYPE##_is_denormal(f1)) { \
1024 return dcmask(4, neg); \
1025 } else if (_TYPE##_is_infinity(f1)) { \
1026 return dcmask(6, neg); \
1027 } else if (_TYPE##_is_quiet_nan(f1, &env->fpu_status)) { \
1028 return dcmask(8, neg); \
1029 } \
1030 /* signaling nan, as last remaining case */ \
1031 return dcmask(10, neg); \
1032 }
1033 DEF_FLOAT_DCMASK(float32)
1034 DEF_FLOAT_DCMASK(float64)
1035 DEF_FLOAT_DCMASK(float128)
1036
1037 /* test data class 32-bit */
1038 uint32_t HELPER(tceb)(CPUS390XState *env, uint64_t f1, uint64_t m2)
1039 {
1040 return (m2 & float32_dcmask(env, f1)) != 0;
1041 }
1042
1043 /* test data class 64-bit */
1044 uint32_t HELPER(tcdb)(CPUS390XState *env, uint64_t v1, uint64_t m2)
1045 {
1046 return (m2 & float64_dcmask(env, v1)) != 0;
1047 }
1048
1049 /* test data class 128-bit */
1050 uint32_t HELPER(tcxb)(CPUS390XState *env, Int128 a, uint64_t m2)
1051 {
1052 return (m2 & float128_dcmask(env, ARG128(a))) != 0;
1053 }
1054
1055 /* square root 32-bit */
1056 uint64_t HELPER(sqeb)(CPUS390XState *env, uint64_t f2)
1057 {
1058 float32 ret = float32_sqrt(f2, &env->fpu_status);
1059 handle_exceptions(env, false, GETPC());
1060 return ret;
1061 }
1062
1063 /* square root 64-bit */
1064 uint64_t HELPER(sqdb)(CPUS390XState *env, uint64_t f2)
1065 {
1066 float64 ret = float64_sqrt(f2, &env->fpu_status);
1067 handle_exceptions(env, false, GETPC());
1068 return ret;
1069 }
1070
1071 /* square root 128-bit */
1072 Int128 HELPER(sqxb)(CPUS390XState *env, Int128 a)
1073 {
1074 float128 ret = float128_sqrt(ARG128(a), &env->fpu_status);
1075 handle_exceptions(env, false, GETPC());
1076 return RET128(ret);
1077 }
1078
1079 static const int fpc_to_rnd[8] = {
1080 float_round_nearest_even,
1081 float_round_to_zero,
1082 float_round_up,
1083 float_round_down,
1084 -1,
1085 -1,
1086 -1,
1087 float_round_to_odd,
1088 };
1089
1090 void cpu_s390x_load_fpc(CPUS390XState *env, uint32_t fpc)
1091 {
1092 /*
1093 * Mimic kernel fpu_lfpc_safe(): a corrupt signal frame value that would
1094 * trigger a specification exception instead results in FPC being set to 0.
1095 */
1096 if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u) {
1097 fpc = 0;
1098 }
1099 env->fpc = fpc;
1100 set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
1101 }
1102
1103 /* set fpc */
1104 void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
1105 {
1106 if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u ||
1107 (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
1108 tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
1109 }
1110 cpu_s390x_load_fpc(env, fpc);
1111 }
1112
1113 /* set fpc and signal */
1114 void HELPER(sfas)(CPUS390XState *env, uint64_t fpc)
1115 {
1116 uint32_t signalling = env->fpc;
1117 uint32_t s390_exc;
1118
1119 if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u ||
1120 (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
1121 tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
1122 }
1123
1124 /*
1125 * FPC is set to the FPC operand with a bitwise OR of the signalling
1126 * flags.
1127 */
1128 env->fpc = fpc | (signalling & 0x00ff0000);
1129 set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
1130
1131 /*
1132 * If any signaling flag is enabled in the new FPC mask, a
1133 * simulated-iee-exception exception occurs.
1134 */
1135 s390_exc = (signalling >> 16) & (fpc >> 24);
1136 if (s390_exc) {
1137 if (s390_exc & S390_IEEE_MASK_INVALID) {
1138 s390_exc = S390_IEEE_MASK_INVALID;
1139 } else if (s390_exc & S390_IEEE_MASK_DIVBYZERO) {
1140 s390_exc = S390_IEEE_MASK_DIVBYZERO;
1141 } else if (s390_exc & S390_IEEE_MASK_OVERFLOW) {
1142 s390_exc &= (S390_IEEE_MASK_OVERFLOW | S390_IEEE_MASK_INEXACT);
1143 } else if (s390_exc & S390_IEEE_MASK_UNDERFLOW) {
1144 s390_exc &= (S390_IEEE_MASK_UNDERFLOW | S390_IEEE_MASK_INEXACT);
1145 } else if (s390_exc & S390_IEEE_MASK_INEXACT) {
1146 s390_exc = S390_IEEE_MASK_INEXACT;
1147 } else if (s390_exc & S390_IEEE_MASK_QUANTUM) {
1148 s390_exc = S390_IEEE_MASK_QUANTUM;
1149 }
1150 tcg_s390_data_exception(env, s390_exc | 3, GETPC());
1151 }
1152 }
1153
1154 /* set bfp rounding mode */
1155 void HELPER(srnm)(CPUS390XState *env, uint64_t rnd)
1156 {
1157 if (rnd > 0x7 || fpc_to_rnd[rnd & 0x7] == -1) {
1158 tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
1159 }
1160
1161 env->fpc = deposit32(env->fpc, 0, 3, rnd);
1162 set_float_rounding_mode(fpc_to_rnd[rnd & 0x7], &env->fpu_status);
1163 }