xref: /openbmc/qemu/target/s390x/tcg/fpu_helper.c (revision b21e2380)
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/exec-all.h"
26 #include "exec/cpu_ldst.h"
27 #include "exec/helper-proto.h"
28 #include "fpu/softfloat.h"
29 
30 /* #define DEBUG_HELPER */
31 #ifdef DEBUG_HELPER
32 #define HELPER_LOG(x...) qemu_log(x)
33 #else
34 #define HELPER_LOG(x...)
35 #endif
36 
37 #define RET128(F) (env->retxl = F.low, F.high)
38 
39 uint8_t s390_softfloat_exc_to_ieee(unsigned int exc)
40 {
41     uint8_t s390_exc = 0;
42 
43     s390_exc |= (exc & float_flag_invalid) ? S390_IEEE_MASK_INVALID : 0;
44     s390_exc |= (exc & float_flag_divbyzero) ? S390_IEEE_MASK_DIVBYZERO : 0;
45     s390_exc |= (exc & float_flag_overflow) ? S390_IEEE_MASK_OVERFLOW : 0;
46     s390_exc |= (exc & float_flag_underflow) ? S390_IEEE_MASK_UNDERFLOW : 0;
47     s390_exc |= (exc & float_flag_inexact) ? S390_IEEE_MASK_INEXACT : 0;
48 
49     return s390_exc;
50 }
51 
52 /* Should be called after any operation that may raise IEEE exceptions.  */
53 static void handle_exceptions(CPUS390XState *env, bool XxC, uintptr_t retaddr)
54 {
55     unsigned s390_exc, qemu_exc;
56 
57     /* Get the exceptions raised by the current operation.  Reset the
58        fpu_status contents so that the next operation has a clean slate.  */
59     qemu_exc = env->fpu_status.float_exception_flags;
60     if (qemu_exc == 0) {
61         return;
62     }
63     env->fpu_status.float_exception_flags = 0;
64     s390_exc = s390_softfloat_exc_to_ieee(qemu_exc);
65 
66     /*
67      * IEEE-Underflow exception recognition exists if a tininess condition
68      * (underflow) exists and
69      * - The mask bit in the FPC is zero and the result is inexact
70      * - The mask bit in the FPC is one
71      * So tininess conditions that are not inexact don't trigger any
72      * underflow action in case the mask bit is not one.
73      */
74     if (!(s390_exc & S390_IEEE_MASK_INEXACT) &&
75         !((env->fpc >> 24) & S390_IEEE_MASK_UNDERFLOW)) {
76         s390_exc &= ~S390_IEEE_MASK_UNDERFLOW;
77     }
78 
79     /*
80      * FIXME:
81      * 1. Right now, all inexact conditions are inidicated as
82      *    "truncated" (0) and never as "incremented" (1) in the DXC.
83      * 2. Only traps due to invalid/divbyzero are suppressing. Other traps
84      *    are completing, meaning the target register has to be written!
85      *    This, however will mean that we have to write the register before
86      *    triggering the trap - impossible right now.
87      */
88 
89     /*
90      * invalid/divbyzero cannot coexist with other conditions.
91      * overflow/underflow however can coexist with inexact, we have to
92      * handle it separatly.
93      */
94     if (s390_exc & ~S390_IEEE_MASK_INEXACT) {
95         if (s390_exc & ~S390_IEEE_MASK_INEXACT & env->fpc >> 24) {
96             /* trap condition - inexact reported along */
97             tcg_s390_data_exception(env, s390_exc, retaddr);
98         }
99         /* nontrap condition - inexact handled differently */
100         env->fpc |= (s390_exc & ~S390_IEEE_MASK_INEXACT) << 16;
101     }
102 
103     /* inexact handling */
104     if (s390_exc & S390_IEEE_MASK_INEXACT && !XxC) {
105         /* trap condition - overflow/underflow _not_ reported along */
106         if (s390_exc & S390_IEEE_MASK_INEXACT & env->fpc >> 24) {
107             tcg_s390_data_exception(env, s390_exc & S390_IEEE_MASK_INEXACT,
108                                     retaddr);
109         }
110         /* nontrap condition */
111         env->fpc |= (s390_exc & S390_IEEE_MASK_INEXACT) << 16;
112     }
113 }
114 
115 int float_comp_to_cc(CPUS390XState *env, FloatRelation float_compare)
116 {
117     switch (float_compare) {
118     case float_relation_equal:
119         return 0;
120     case float_relation_less:
121         return 1;
122     case float_relation_greater:
123         return 2;
124     case float_relation_unordered:
125         return 3;
126     default:
127         cpu_abort(env_cpu(env), "unknown return value for float compare\n");
128     }
129 }
130 
131 /* condition codes for unary FP ops */
132 uint32_t set_cc_nz_f32(float32 v)
133 {
134     if (float32_is_any_nan(v)) {
135         return 3;
136     } else if (float32_is_zero(v)) {
137         return 0;
138     } else if (float32_is_neg(v)) {
139         return 1;
140     } else {
141         return 2;
142     }
143 }
144 
145 uint32_t set_cc_nz_f64(float64 v)
146 {
147     if (float64_is_any_nan(v)) {
148         return 3;
149     } else if (float64_is_zero(v)) {
150         return 0;
151     } else if (float64_is_neg(v)) {
152         return 1;
153     } else {
154         return 2;
155     }
156 }
157 
158 uint32_t set_cc_nz_f128(float128 v)
159 {
160     if (float128_is_any_nan(v)) {
161         return 3;
162     } else if (float128_is_zero(v)) {
163         return 0;
164     } else if (float128_is_neg(v)) {
165         return 1;
166     } else {
167         return 2;
168     }
169 }
170 
171 /* condition codes for FP to integer conversion ops */
172 static uint32_t set_cc_conv_f32(float32 v, float_status *stat)
173 {
174     if (stat->float_exception_flags & float_flag_invalid) {
175         return 3;
176     } else {
177         return set_cc_nz_f32(v);
178     }
179 }
180 
181 static uint32_t set_cc_conv_f64(float64 v, float_status *stat)
182 {
183     if (stat->float_exception_flags & float_flag_invalid) {
184         return 3;
185     } else {
186         return set_cc_nz_f64(v);
187     }
188 }
189 
190 static uint32_t set_cc_conv_f128(float128 v, float_status *stat)
191 {
192     if (stat->float_exception_flags & float_flag_invalid) {
193         return 3;
194     } else {
195         return set_cc_nz_f128(v);
196     }
197 }
198 
199 static inline uint8_t round_from_m34(uint32_t m34)
200 {
201     return extract32(m34, 0, 4);
202 }
203 
204 static inline bool xxc_from_m34(uint32_t m34)
205 {
206     /* XxC is bit 1 of m4 */
207     return extract32(m34, 4 + 3 - 1, 1);
208 }
209 
210 /* 32-bit FP addition */
211 uint64_t HELPER(aeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
212 {
213     float32 ret = float32_add(f1, f2, &env->fpu_status);
214     handle_exceptions(env, false, GETPC());
215     return ret;
216 }
217 
218 /* 64-bit FP addition */
219 uint64_t HELPER(adb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
220 {
221     float64 ret = float64_add(f1, f2, &env->fpu_status);
222     handle_exceptions(env, false, GETPC());
223     return ret;
224 }
225 
226 /* 128-bit FP addition */
227 uint64_t HELPER(axb)(CPUS390XState *env, uint64_t ah, uint64_t al,
228                      uint64_t bh, uint64_t bl)
229 {
230     float128 ret = float128_add(make_float128(ah, al),
231                                 make_float128(bh, bl),
232                                 &env->fpu_status);
233     handle_exceptions(env, false, GETPC());
234     return RET128(ret);
235 }
236 
237 /* 32-bit FP subtraction */
238 uint64_t HELPER(seb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
239 {
240     float32 ret = float32_sub(f1, f2, &env->fpu_status);
241     handle_exceptions(env, false, GETPC());
242     return ret;
243 }
244 
245 /* 64-bit FP subtraction */
246 uint64_t HELPER(sdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
247 {
248     float64 ret = float64_sub(f1, f2, &env->fpu_status);
249     handle_exceptions(env, false, GETPC());
250     return ret;
251 }
252 
253 /* 128-bit FP subtraction */
254 uint64_t HELPER(sxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
255                      uint64_t bh, uint64_t bl)
256 {
257     float128 ret = float128_sub(make_float128(ah, al),
258                                 make_float128(bh, bl),
259                                 &env->fpu_status);
260     handle_exceptions(env, false, GETPC());
261     return RET128(ret);
262 }
263 
264 /* 32-bit FP division */
265 uint64_t HELPER(deb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
266 {
267     float32 ret = float32_div(f1, f2, &env->fpu_status);
268     handle_exceptions(env, false, GETPC());
269     return ret;
270 }
271 
272 /* 64-bit FP division */
273 uint64_t HELPER(ddb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
274 {
275     float64 ret = float64_div(f1, f2, &env->fpu_status);
276     handle_exceptions(env, false, GETPC());
277     return ret;
278 }
279 
280 /* 128-bit FP division */
281 uint64_t HELPER(dxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
282                      uint64_t bh, uint64_t bl)
283 {
284     float128 ret = float128_div(make_float128(ah, al),
285                                 make_float128(bh, bl),
286                                 &env->fpu_status);
287     handle_exceptions(env, false, GETPC());
288     return RET128(ret);
289 }
290 
291 /* 32-bit FP multiplication */
292 uint64_t HELPER(meeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
293 {
294     float32 ret = float32_mul(f1, f2, &env->fpu_status);
295     handle_exceptions(env, false, GETPC());
296     return ret;
297 }
298 
299 /* 64-bit FP multiplication */
300 uint64_t HELPER(mdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
301 {
302     float64 ret = float64_mul(f1, f2, &env->fpu_status);
303     handle_exceptions(env, false, GETPC());
304     return ret;
305 }
306 
307 /* 64/32-bit FP multiplication */
308 uint64_t HELPER(mdeb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
309 {
310     float64 ret = float32_to_float64(f2, &env->fpu_status);
311     ret = float64_mul(f1, ret, &env->fpu_status);
312     handle_exceptions(env, false, GETPC());
313     return ret;
314 }
315 
316 /* 128-bit FP multiplication */
317 uint64_t HELPER(mxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
318                      uint64_t bh, uint64_t bl)
319 {
320     float128 ret = float128_mul(make_float128(ah, al),
321                                 make_float128(bh, bl),
322                                 &env->fpu_status);
323     handle_exceptions(env, false, GETPC());
324     return RET128(ret);
325 }
326 
327 /* 128/64-bit FP multiplication */
328 uint64_t HELPER(mxdb)(CPUS390XState *env, uint64_t ah, uint64_t al,
329                       uint64_t f2)
330 {
331     float128 ret = float64_to_float128(f2, &env->fpu_status);
332     ret = float128_mul(make_float128(ah, al), ret, &env->fpu_status);
333     handle_exceptions(env, false, GETPC());
334     return RET128(ret);
335 }
336 
337 /* convert 32-bit float to 64-bit float */
338 uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2)
339 {
340     float64 ret = float32_to_float64(f2, &env->fpu_status);
341     handle_exceptions(env, false, GETPC());
342     return ret;
343 }
344 
345 /* convert 128-bit float to 64-bit float */
346 uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
347                       uint32_t m34)
348 {
349     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
350     float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status);
351 
352     s390_restore_bfp_rounding_mode(env, old_mode);
353     handle_exceptions(env, xxc_from_m34(m34), GETPC());
354     return ret;
355 }
356 
357 /* convert 64-bit float to 128-bit float */
358 uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2)
359 {
360     float128 ret = float64_to_float128(f2, &env->fpu_status);
361     handle_exceptions(env, false, GETPC());
362     return RET128(ret);
363 }
364 
365 /* convert 32-bit float to 128-bit float */
366 uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2)
367 {
368     float128 ret = float32_to_float128(f2, &env->fpu_status);
369     handle_exceptions(env, false, GETPC());
370     return RET128(ret);
371 }
372 
373 /* convert 64-bit float to 32-bit float */
374 uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2, uint32_t m34)
375 {
376     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
377     float32 ret = float64_to_float32(f2, &env->fpu_status);
378 
379     s390_restore_bfp_rounding_mode(env, old_mode);
380     handle_exceptions(env, xxc_from_m34(m34), GETPC());
381     return ret;
382 }
383 
384 /* convert 128-bit float to 32-bit float */
385 uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al,
386                       uint32_t m34)
387 {
388     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
389     float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status);
390 
391     s390_restore_bfp_rounding_mode(env, old_mode);
392     handle_exceptions(env, xxc_from_m34(m34), GETPC());
393     return ret;
394 }
395 
396 /* 32-bit FP compare */
397 uint32_t HELPER(ceb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
398 {
399     FloatRelation cmp = float32_compare_quiet(f1, f2, &env->fpu_status);
400     handle_exceptions(env, false, GETPC());
401     return float_comp_to_cc(env, cmp);
402 }
403 
404 /* 64-bit FP compare */
405 uint32_t HELPER(cdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
406 {
407     FloatRelation cmp = float64_compare_quiet(f1, f2, &env->fpu_status);
408     handle_exceptions(env, false, GETPC());
409     return float_comp_to_cc(env, cmp);
410 }
411 
412 /* 128-bit FP compare */
413 uint32_t HELPER(cxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
414                      uint64_t bh, uint64_t bl)
415 {
416     FloatRelation cmp = float128_compare_quiet(make_float128(ah, al),
417                                                make_float128(bh, bl),
418                                                &env->fpu_status);
419     handle_exceptions(env, false, GETPC());
420     return float_comp_to_cc(env, cmp);
421 }
422 
423 int s390_swap_bfp_rounding_mode(CPUS390XState *env, int m3)
424 {
425     int ret = env->fpu_status.float_rounding_mode;
426 
427     switch (m3) {
428     case 0:
429         /* current mode */
430         break;
431     case 1:
432         /* round to nearest with ties away from 0 */
433         set_float_rounding_mode(float_round_ties_away, &env->fpu_status);
434         break;
435     case 3:
436         /* round to prepare for shorter precision */
437         set_float_rounding_mode(float_round_to_odd, &env->fpu_status);
438         break;
439     case 4:
440         /* round to nearest with ties to even */
441         set_float_rounding_mode(float_round_nearest_even, &env->fpu_status);
442         break;
443     case 5:
444         /* round to zero */
445         set_float_rounding_mode(float_round_to_zero, &env->fpu_status);
446         break;
447     case 6:
448         /* round to +inf */
449         set_float_rounding_mode(float_round_up, &env->fpu_status);
450         break;
451     case 7:
452         /* round to -inf */
453         set_float_rounding_mode(float_round_down, &env->fpu_status);
454         break;
455     default:
456         g_assert_not_reached();
457     }
458     return ret;
459 }
460 
461 void s390_restore_bfp_rounding_mode(CPUS390XState *env, int old_mode)
462 {
463     set_float_rounding_mode(old_mode, &env->fpu_status);
464 }
465 
466 /* convert 64-bit int to 32-bit float */
467 uint64_t HELPER(cegb)(CPUS390XState *env, int64_t v2, uint32_t m34)
468 {
469     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
470     float32 ret = int64_to_float32(v2, &env->fpu_status);
471 
472     s390_restore_bfp_rounding_mode(env, old_mode);
473     handle_exceptions(env, xxc_from_m34(m34), GETPC());
474     return ret;
475 }
476 
477 /* convert 64-bit int to 64-bit float */
478 uint64_t HELPER(cdgb)(CPUS390XState *env, int64_t v2, uint32_t m34)
479 {
480     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
481     float64 ret = int64_to_float64(v2, &env->fpu_status);
482 
483     s390_restore_bfp_rounding_mode(env, old_mode);
484     handle_exceptions(env, xxc_from_m34(m34), GETPC());
485     return ret;
486 }
487 
488 /* convert 64-bit int to 128-bit float */
489 uint64_t HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m34)
490 {
491     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
492     float128 ret = int64_to_float128(v2, &env->fpu_status);
493 
494     s390_restore_bfp_rounding_mode(env, old_mode);
495     handle_exceptions(env, xxc_from_m34(m34), GETPC());
496     return RET128(ret);
497 }
498 
499 /* convert 64-bit uint to 32-bit float */
500 uint64_t HELPER(celgb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
501 {
502     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
503     float32 ret = uint64_to_float32(v2, &env->fpu_status);
504 
505     s390_restore_bfp_rounding_mode(env, old_mode);
506     handle_exceptions(env, xxc_from_m34(m34), GETPC());
507     return ret;
508 }
509 
510 /* convert 64-bit uint to 64-bit float */
511 uint64_t HELPER(cdlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
512 {
513     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
514     float64 ret = uint64_to_float64(v2, &env->fpu_status);
515 
516     s390_restore_bfp_rounding_mode(env, old_mode);
517     handle_exceptions(env, xxc_from_m34(m34), GETPC());
518     return ret;
519 }
520 
521 /* convert 64-bit uint to 128-bit float */
522 uint64_t HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
523 {
524     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
525     float128 ret = uint64_to_float128(v2, &env->fpu_status);
526 
527     s390_restore_bfp_rounding_mode(env, old_mode);
528     handle_exceptions(env, xxc_from_m34(m34), GETPC());
529     return RET128(ret);
530 }
531 
532 /* convert 32-bit float to 64-bit int */
533 uint64_t HELPER(cgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
534 {
535     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
536     int64_t ret = float32_to_int64(v2, &env->fpu_status);
537     uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status);
538 
539     s390_restore_bfp_rounding_mode(env, old_mode);
540     handle_exceptions(env, xxc_from_m34(m34), GETPC());
541     env->cc_op = cc;
542     if (float32_is_any_nan(v2)) {
543         return INT64_MIN;
544     }
545     return ret;
546 }
547 
548 /* convert 64-bit float to 64-bit int */
549 uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
550 {
551     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
552     int64_t ret = float64_to_int64(v2, &env->fpu_status);
553     uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status);
554 
555     s390_restore_bfp_rounding_mode(env, old_mode);
556     handle_exceptions(env, xxc_from_m34(m34), GETPC());
557     env->cc_op = cc;
558     if (float64_is_any_nan(v2)) {
559         return INT64_MIN;
560     }
561     return ret;
562 }
563 
564 /* convert 128-bit float to 64-bit int */
565 uint64_t HELPER(cgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34)
566 {
567     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
568     float128 v2 = make_float128(h, l);
569     int64_t ret = float128_to_int64(v2, &env->fpu_status);
570     uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status);
571 
572     s390_restore_bfp_rounding_mode(env, old_mode);
573     handle_exceptions(env, xxc_from_m34(m34), GETPC());
574     env->cc_op = cc;
575     if (float128_is_any_nan(v2)) {
576         return INT64_MIN;
577     }
578     return ret;
579 }
580 
581 /* convert 32-bit float to 32-bit int */
582 uint64_t HELPER(cfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
583 {
584     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
585     int32_t ret = float32_to_int32(v2, &env->fpu_status);
586     uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status);
587 
588     s390_restore_bfp_rounding_mode(env, old_mode);
589     handle_exceptions(env, xxc_from_m34(m34), GETPC());
590     env->cc_op = cc;
591     if (float32_is_any_nan(v2)) {
592         return INT32_MIN;
593     }
594     return ret;
595 }
596 
597 /* convert 64-bit float to 32-bit int */
598 uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
599 {
600     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
601     int32_t ret = float64_to_int32(v2, &env->fpu_status);
602     uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status);
603 
604     s390_restore_bfp_rounding_mode(env, old_mode);
605     handle_exceptions(env, xxc_from_m34(m34), GETPC());
606     env->cc_op = cc;
607     if (float64_is_any_nan(v2)) {
608         return INT32_MIN;
609     }
610     return ret;
611 }
612 
613 /* convert 128-bit float to 32-bit int */
614 uint64_t HELPER(cfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34)
615 {
616     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
617     float128 v2 = make_float128(h, l);
618     int32_t ret = float128_to_int32(v2, &env->fpu_status);
619     uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status);
620 
621     s390_restore_bfp_rounding_mode(env, old_mode);
622     handle_exceptions(env, xxc_from_m34(m34), GETPC());
623     env->cc_op = cc;
624     if (float128_is_any_nan(v2)) {
625         return INT32_MIN;
626     }
627     return ret;
628 }
629 
630 /* convert 32-bit float to 64-bit uint */
631 uint64_t HELPER(clgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
632 {
633     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
634     uint64_t ret = float32_to_uint64(v2, &env->fpu_status);
635     uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status);
636 
637     s390_restore_bfp_rounding_mode(env, old_mode);
638     handle_exceptions(env, xxc_from_m34(m34), GETPC());
639     env->cc_op = cc;
640     if (float32_is_any_nan(v2)) {
641         return 0;
642     }
643     return ret;
644 }
645 
646 /* convert 64-bit float to 64-bit uint */
647 uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
648 {
649     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
650     uint64_t ret = float64_to_uint64(v2, &env->fpu_status);
651     uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status);
652 
653     s390_restore_bfp_rounding_mode(env, old_mode);
654     handle_exceptions(env, xxc_from_m34(m34), GETPC());
655     env->cc_op = cc;
656     if (float64_is_any_nan(v2)) {
657         return 0;
658     }
659     return ret;
660 }
661 
662 /* convert 128-bit float to 64-bit uint */
663 uint64_t HELPER(clgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34)
664 {
665     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
666     float128 v2 = make_float128(h, l);
667     uint64_t ret = float128_to_uint64(v2, &env->fpu_status);
668     uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status);
669 
670     s390_restore_bfp_rounding_mode(env, old_mode);
671     handle_exceptions(env, xxc_from_m34(m34), GETPC());
672     env->cc_op = cc;
673     if (float128_is_any_nan(v2)) {
674         return 0;
675     }
676     return ret;
677 }
678 
679 /* convert 32-bit float to 32-bit uint */
680 uint64_t HELPER(clfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
681 {
682     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
683     uint32_t ret = float32_to_uint32(v2, &env->fpu_status);
684     uint32_t cc = set_cc_conv_f32(v2, &env->fpu_status);
685 
686     s390_restore_bfp_rounding_mode(env, old_mode);
687     handle_exceptions(env, xxc_from_m34(m34), GETPC());
688     env->cc_op = cc;
689     if (float32_is_any_nan(v2)) {
690         return 0;
691     }
692     return ret;
693 }
694 
695 /* convert 64-bit float to 32-bit uint */
696 uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34)
697 {
698     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
699     uint32_t ret = float64_to_uint32(v2, &env->fpu_status);
700     uint32_t cc = set_cc_conv_f64(v2, &env->fpu_status);
701 
702     s390_restore_bfp_rounding_mode(env, old_mode);
703     handle_exceptions(env, xxc_from_m34(m34), GETPC());
704     env->cc_op = cc;
705     if (float64_is_any_nan(v2)) {
706         return 0;
707     }
708     return ret;
709 }
710 
711 /* convert 128-bit float to 32-bit uint */
712 uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34)
713 {
714     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
715     float128 v2 = make_float128(h, l);
716     uint32_t ret = float128_to_uint32(v2, &env->fpu_status);
717     uint32_t cc = set_cc_conv_f128(v2, &env->fpu_status);
718 
719     s390_restore_bfp_rounding_mode(env, old_mode);
720     handle_exceptions(env, xxc_from_m34(m34), GETPC());
721     env->cc_op = cc;
722     if (float128_is_any_nan(v2)) {
723         return 0;
724     }
725     return ret;
726 }
727 
728 /* round to integer 32-bit */
729 uint64_t HELPER(fieb)(CPUS390XState *env, uint64_t f2, uint32_t m34)
730 {
731     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
732     float32 ret = float32_round_to_int(f2, &env->fpu_status);
733 
734     s390_restore_bfp_rounding_mode(env, old_mode);
735     handle_exceptions(env, xxc_from_m34(m34), GETPC());
736     return ret;
737 }
738 
739 /* round to integer 64-bit */
740 uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m34)
741 {
742     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
743     float64 ret = float64_round_to_int(f2, &env->fpu_status);
744 
745     s390_restore_bfp_rounding_mode(env, old_mode);
746     handle_exceptions(env, xxc_from_m34(m34), GETPC());
747     return ret;
748 }
749 
750 /* round to integer 128-bit */
751 uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al,
752                       uint32_t m34)
753 {
754     int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34));
755     float128 ret = float128_round_to_int(make_float128(ah, al),
756                                          &env->fpu_status);
757 
758     s390_restore_bfp_rounding_mode(env, old_mode);
759     handle_exceptions(env, xxc_from_m34(m34), GETPC());
760     return RET128(ret);
761 }
762 
763 /* 32-bit FP compare and signal */
764 uint32_t HELPER(keb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
765 {
766     FloatRelation cmp = float32_compare(f1, f2, &env->fpu_status);
767     handle_exceptions(env, false, GETPC());
768     return float_comp_to_cc(env, cmp);
769 }
770 
771 /* 64-bit FP compare and signal */
772 uint32_t HELPER(kdb)(CPUS390XState *env, uint64_t f1, uint64_t f2)
773 {
774     FloatRelation cmp = float64_compare(f1, f2, &env->fpu_status);
775     handle_exceptions(env, false, GETPC());
776     return float_comp_to_cc(env, cmp);
777 }
778 
779 /* 128-bit FP compare and signal */
780 uint32_t HELPER(kxb)(CPUS390XState *env, uint64_t ah, uint64_t al,
781                      uint64_t bh, uint64_t bl)
782 {
783     FloatRelation cmp = float128_compare(make_float128(ah, al),
784                                          make_float128(bh, bl),
785                                          &env->fpu_status);
786     handle_exceptions(env, false, GETPC());
787     return float_comp_to_cc(env, cmp);
788 }
789 
790 /* 32-bit FP multiply and add */
791 uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1,
792                       uint64_t f2, uint64_t f3)
793 {
794     float32 ret = float32_muladd(f2, f3, f1, 0, &env->fpu_status);
795     handle_exceptions(env, false, GETPC());
796     return ret;
797 }
798 
799 /* 64-bit FP multiply and add */
800 uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1,
801                       uint64_t f2, uint64_t f3)
802 {
803     float64 ret = float64_muladd(f2, f3, f1, 0, &env->fpu_status);
804     handle_exceptions(env, false, GETPC());
805     return ret;
806 }
807 
808 /* 32-bit FP multiply and subtract */
809 uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1,
810                       uint64_t f2, uint64_t f3)
811 {
812     float32 ret = float32_muladd(f2, f3, f1, float_muladd_negate_c,
813                                  &env->fpu_status);
814     handle_exceptions(env, false, GETPC());
815     return ret;
816 }
817 
818 /* 64-bit FP multiply and subtract */
819 uint64_t HELPER(msdb)(CPUS390XState *env, uint64_t f1,
820                       uint64_t f2, uint64_t f3)
821 {
822     float64 ret = float64_muladd(f2, f3, f1, float_muladd_negate_c,
823                                  &env->fpu_status);
824     handle_exceptions(env, false, GETPC());
825     return ret;
826 }
827 
828 /* The rightmost bit has the number 11. */
829 static inline uint16_t dcmask(int bit, bool neg)
830 {
831     return 1 << (11 - bit - neg);
832 }
833 
834 #define DEF_FLOAT_DCMASK(_TYPE) \
835 uint16_t _TYPE##_dcmask(CPUS390XState *env, _TYPE f1)              \
836 {                                                                  \
837     const bool neg = _TYPE##_is_neg(f1);                           \
838                                                                    \
839     /* Sorted by most common cases - only one class is possible */ \
840     if (_TYPE##_is_normal(f1)) {                                   \
841         return dcmask(2, neg);                                     \
842     } else if (_TYPE##_is_zero(f1)) {                              \
843         return dcmask(0, neg);                                     \
844     } else if (_TYPE##_is_denormal(f1)) {                          \
845         return dcmask(4, neg);                                     \
846     } else if (_TYPE##_is_infinity(f1)) {                          \
847         return dcmask(6, neg);                                     \
848     } else if (_TYPE##_is_quiet_nan(f1, &env->fpu_status)) {       \
849         return dcmask(8, neg);                                     \
850     }                                                              \
851     /* signaling nan, as last remaining case */                    \
852     return dcmask(10, neg);                                        \
853 }
854 DEF_FLOAT_DCMASK(float32)
855 DEF_FLOAT_DCMASK(float64)
856 DEF_FLOAT_DCMASK(float128)
857 
858 /* test data class 32-bit */
859 uint32_t HELPER(tceb)(CPUS390XState *env, uint64_t f1, uint64_t m2)
860 {
861     return (m2 & float32_dcmask(env, f1)) != 0;
862 }
863 
864 /* test data class 64-bit */
865 uint32_t HELPER(tcdb)(CPUS390XState *env, uint64_t v1, uint64_t m2)
866 {
867     return (m2 & float64_dcmask(env, v1)) != 0;
868 }
869 
870 /* test data class 128-bit */
871 uint32_t HELPER(tcxb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t m2)
872 {
873     return (m2 & float128_dcmask(env, make_float128(ah, al))) != 0;
874 }
875 
876 /* square root 32-bit */
877 uint64_t HELPER(sqeb)(CPUS390XState *env, uint64_t f2)
878 {
879     float32 ret = float32_sqrt(f2, &env->fpu_status);
880     handle_exceptions(env, false, GETPC());
881     return ret;
882 }
883 
884 /* square root 64-bit */
885 uint64_t HELPER(sqdb)(CPUS390XState *env, uint64_t f2)
886 {
887     float64 ret = float64_sqrt(f2, &env->fpu_status);
888     handle_exceptions(env, false, GETPC());
889     return ret;
890 }
891 
892 /* square root 128-bit */
893 uint64_t HELPER(sqxb)(CPUS390XState *env, uint64_t ah, uint64_t al)
894 {
895     float128 ret = float128_sqrt(make_float128(ah, al), &env->fpu_status);
896     handle_exceptions(env, false, GETPC());
897     return RET128(ret);
898 }
899 
900 static const int fpc_to_rnd[8] = {
901     float_round_nearest_even,
902     float_round_to_zero,
903     float_round_up,
904     float_round_down,
905     -1,
906     -1,
907     -1,
908     float_round_to_odd,
909 };
910 
911 /* set fpc */
912 void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc)
913 {
914     if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u ||
915         (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
916         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
917     }
918 
919     /* Install everything in the main FPC.  */
920     env->fpc = fpc;
921 
922     /* Install the rounding mode in the shadow fpu_status.  */
923     set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
924 }
925 
926 /* set fpc and signal */
927 void HELPER(sfas)(CPUS390XState *env, uint64_t fpc)
928 {
929     uint32_t signalling = env->fpc;
930     uint32_t s390_exc;
931 
932     if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u ||
933         (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) {
934         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
935     }
936 
937     /*
938      * FPC is set to the FPC operand with a bitwise OR of the signalling
939      * flags.
940      */
941     env->fpc = fpc | (signalling & 0x00ff0000);
942     set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status);
943 
944     /*
945      * If any signaling flag is enabled in the new FPC mask, a
946      * simulated-iee-exception exception occurs.
947      */
948     s390_exc = (signalling >> 16) & (fpc >> 24);
949     if (s390_exc) {
950         if (s390_exc & S390_IEEE_MASK_INVALID) {
951             s390_exc = S390_IEEE_MASK_INVALID;
952         } else if (s390_exc & S390_IEEE_MASK_DIVBYZERO) {
953             s390_exc = S390_IEEE_MASK_DIVBYZERO;
954         } else if (s390_exc & S390_IEEE_MASK_OVERFLOW) {
955             s390_exc &= (S390_IEEE_MASK_OVERFLOW | S390_IEEE_MASK_INEXACT);
956         } else if (s390_exc & S390_IEEE_MASK_UNDERFLOW) {
957             s390_exc &= (S390_IEEE_MASK_UNDERFLOW | S390_IEEE_MASK_INEXACT);
958         } else if (s390_exc & S390_IEEE_MASK_INEXACT) {
959             s390_exc = S390_IEEE_MASK_INEXACT;
960         } else if (s390_exc & S390_IEEE_MASK_QUANTUM) {
961             s390_exc = S390_IEEE_MASK_QUANTUM;
962         }
963         tcg_s390_data_exception(env, s390_exc | 3, GETPC());
964     }
965 }
966 
967 /* set bfp rounding mode */
968 void HELPER(srnm)(CPUS390XState *env, uint64_t rnd)
969 {
970     if (rnd > 0x7 || fpc_to_rnd[rnd & 0x7] == -1) {
971         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
972     }
973 
974     env->fpc = deposit32(env->fpc, 0, 3, rnd);
975     set_float_rounding_mode(fpc_to_rnd[rnd & 0x7], &env->fpu_status);
976 }
977