xref: /openbmc/qemu/target/s390x/tcg/vec_fpu_helper.c (revision b14df228)
1 /*
2  * QEMU TCG support -- s390x vector floating point instruction support
3  *
4  * Copyright (C) 2019 Red Hat Inc
5  *
6  * Authors:
7  *   David Hildenbrand <david@redhat.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12 #include "qemu/osdep.h"
13 #include "cpu.h"
14 #include "s390x-internal.h"
15 #include "vec.h"
16 #include "tcg_s390x.h"
17 #include "tcg/tcg-gvec-desc.h"
18 #include "exec/exec-all.h"
19 #include "exec/helper-proto.h"
20 #include "fpu/softfloat.h"
21 
22 #define VIC_INVALID         0x1
23 #define VIC_DIVBYZERO       0x2
24 #define VIC_OVERFLOW        0x3
25 #define VIC_UNDERFLOW       0x4
26 #define VIC_INEXACT         0x5
27 
28 /* returns the VEX. If the VEX is 0, there is no trap */
29 static uint8_t check_ieee_exc(CPUS390XState *env, uint8_t enr, bool XxC,
30                               uint8_t *vec_exc)
31 {
32     uint8_t vece_exc = 0, trap_exc;
33     unsigned qemu_exc;
34 
35     /* Retrieve and clear the softfloat exceptions */
36     qemu_exc = env->fpu_status.float_exception_flags;
37     if (qemu_exc == 0) {
38         return 0;
39     }
40     env->fpu_status.float_exception_flags = 0;
41 
42     vece_exc = s390_softfloat_exc_to_ieee(qemu_exc);
43 
44     /* Add them to the vector-wide s390x exception bits */
45     *vec_exc |= vece_exc;
46 
47     /* Check for traps and construct the VXC */
48     trap_exc = vece_exc & env->fpc >> 24;
49     if (trap_exc) {
50         if (trap_exc & S390_IEEE_MASK_INVALID) {
51             return enr << 4 | VIC_INVALID;
52         } else if (trap_exc & S390_IEEE_MASK_DIVBYZERO) {
53             return enr << 4 | VIC_DIVBYZERO;
54         } else if (trap_exc & S390_IEEE_MASK_OVERFLOW) {
55             return enr << 4 | VIC_OVERFLOW;
56         } else if (trap_exc & S390_IEEE_MASK_UNDERFLOW) {
57             return enr << 4 | VIC_UNDERFLOW;
58         } else if (!XxC) {
59             g_assert(trap_exc & S390_IEEE_MASK_INEXACT);
60             /* inexact has lowest priority on traps */
61             return enr << 4 | VIC_INEXACT;
62         }
63     }
64     return 0;
65 }
66 
67 static void handle_ieee_exc(CPUS390XState *env, uint8_t vxc, uint8_t vec_exc,
68                             uintptr_t retaddr)
69 {
70     if (vxc) {
71         /* on traps, the fpc flags are not updated, instruction is suppressed */
72         tcg_s390_vector_exception(env, vxc, retaddr);
73     }
74     if (vec_exc) {
75         /* indicate exceptions for all elements combined */
76         env->fpc |= vec_exc << 16;
77     }
78 }
79 
80 static float32 s390_vec_read_float32(const S390Vector *v, uint8_t enr)
81 {
82     return make_float32(s390_vec_read_element32(v, enr));
83 }
84 
85 static float64 s390_vec_read_float64(const S390Vector *v, uint8_t enr)
86 {
87     return make_float64(s390_vec_read_element64(v, enr));
88 }
89 
90 static float128 s390_vec_read_float128(const S390Vector *v)
91 {
92     return make_float128(s390_vec_read_element64(v, 0),
93                          s390_vec_read_element64(v, 1));
94 }
95 
96 static void s390_vec_write_float32(S390Vector *v, uint8_t enr, float32 data)
97 {
98     return s390_vec_write_element32(v, enr, data);
99 }
100 
101 static void s390_vec_write_float64(S390Vector *v, uint8_t enr, float64 data)
102 {
103     return s390_vec_write_element64(v, enr, data);
104 }
105 
106 static void s390_vec_write_float128(S390Vector *v, float128 data)
107 {
108     s390_vec_write_element64(v, 0, data.high);
109     s390_vec_write_element64(v, 1, data.low);
110 }
111 
112 typedef float32 (*vop32_2_fn)(float32 a, float_status *s);
113 static void vop32_2(S390Vector *v1, const S390Vector *v2, CPUS390XState *env,
114                     bool s, bool XxC, uint8_t erm, vop32_2_fn fn,
115                     uintptr_t retaddr)
116 {
117     uint8_t vxc, vec_exc = 0;
118     S390Vector tmp = {};
119     int i, old_mode;
120 
121     old_mode = s390_swap_bfp_rounding_mode(env, erm);
122     for (i = 0; i < 4; i++) {
123         const float32 a = s390_vec_read_float32(v2, i);
124 
125         s390_vec_write_float32(&tmp, i, fn(a, &env->fpu_status));
126         vxc = check_ieee_exc(env, i, XxC, &vec_exc);
127         if (s || vxc) {
128             break;
129         }
130     }
131     s390_restore_bfp_rounding_mode(env, old_mode);
132     handle_ieee_exc(env, vxc, vec_exc, retaddr);
133     *v1 = tmp;
134 }
135 
136 typedef float64 (*vop64_2_fn)(float64 a, float_status *s);
137 static void vop64_2(S390Vector *v1, const S390Vector *v2, CPUS390XState *env,
138                     bool s, bool XxC, uint8_t erm, vop64_2_fn fn,
139                     uintptr_t retaddr)
140 {
141     uint8_t vxc, vec_exc = 0;
142     S390Vector tmp = {};
143     int i, old_mode;
144 
145     old_mode = s390_swap_bfp_rounding_mode(env, erm);
146     for (i = 0; i < 2; i++) {
147         const float64 a = s390_vec_read_float64(v2, i);
148 
149         s390_vec_write_float64(&tmp, i, fn(a, &env->fpu_status));
150         vxc = check_ieee_exc(env, i, XxC, &vec_exc);
151         if (s || vxc) {
152             break;
153         }
154     }
155     s390_restore_bfp_rounding_mode(env, old_mode);
156     handle_ieee_exc(env, vxc, vec_exc, retaddr);
157     *v1 = tmp;
158 }
159 
160 typedef float128 (*vop128_2_fn)(float128 a, float_status *s);
161 static void vop128_2(S390Vector *v1, const S390Vector *v2, CPUS390XState *env,
162                     bool s, bool XxC, uint8_t erm, vop128_2_fn fn,
163                     uintptr_t retaddr)
164 {
165     const float128 a = s390_vec_read_float128(v2);
166     uint8_t vxc, vec_exc = 0;
167     S390Vector tmp = {};
168     int old_mode;
169 
170     old_mode = s390_swap_bfp_rounding_mode(env, erm);
171     s390_vec_write_float128(&tmp, fn(a, &env->fpu_status));
172     vxc = check_ieee_exc(env, 0, XxC, &vec_exc);
173     s390_restore_bfp_rounding_mode(env, old_mode);
174     handle_ieee_exc(env, vxc, vec_exc, retaddr);
175     *v1 = tmp;
176 }
177 
178 static float32 vcdg32(float32 a, float_status *s)
179 {
180     return int32_to_float32(a, s);
181 }
182 
183 static float32 vcdlg32(float32 a, float_status *s)
184 {
185     return uint32_to_float32(a, s);
186 }
187 
188 static float32 vcgd32(float32 a, float_status *s)
189 {
190     const float32 tmp = float32_to_int32(a, s);
191 
192     return float32_is_any_nan(a) ? INT32_MIN : tmp;
193 }
194 
195 static float32 vclgd32(float32 a, float_status *s)
196 {
197     const float32 tmp = float32_to_uint32(a, s);
198 
199     return float32_is_any_nan(a) ? 0 : tmp;
200 }
201 
202 static float64 vcdg64(float64 a, float_status *s)
203 {
204     return int64_to_float64(a, s);
205 }
206 
207 static float64 vcdlg64(float64 a, float_status *s)
208 {
209     return uint64_to_float64(a, s);
210 }
211 
212 static float64 vcgd64(float64 a, float_status *s)
213 {
214     const float64 tmp = float64_to_int64(a, s);
215 
216     return float64_is_any_nan(a) ? INT64_MIN : tmp;
217 }
218 
219 static float64 vclgd64(float64 a, float_status *s)
220 {
221     const float64 tmp = float64_to_uint64(a, s);
222 
223     return float64_is_any_nan(a) ? 0 : tmp;
224 }
225 
226 #define DEF_GVEC_VOP2_FN(NAME, FN, BITS)                                       \
227 void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, CPUS390XState *env,   \
228                                uint32_t desc)                                  \
229 {                                                                              \
230     const uint8_t erm = extract32(simd_data(desc), 4, 4);                      \
231     const bool se = extract32(simd_data(desc), 3, 1);                          \
232     const bool XxC = extract32(simd_data(desc), 2, 1);                         \
233                                                                                \
234     vop##BITS##_2(v1, v2, env, se, XxC, erm, FN, GETPC());                     \
235 }
236 
237 #define DEF_GVEC_VOP2_32(NAME)                                                 \
238 DEF_GVEC_VOP2_FN(NAME, NAME##32, 32)
239 
240 #define DEF_GVEC_VOP2_64(NAME)                                                 \
241 DEF_GVEC_VOP2_FN(NAME, NAME##64, 64)
242 
243 #define DEF_GVEC_VOP2(NAME, OP)                                                \
244 DEF_GVEC_VOP2_FN(NAME, float32_##OP, 32)                                       \
245 DEF_GVEC_VOP2_FN(NAME, float64_##OP, 64)                                       \
246 DEF_GVEC_VOP2_FN(NAME, float128_##OP, 128)
247 
248 DEF_GVEC_VOP2_32(vcdg)
249 DEF_GVEC_VOP2_32(vcdlg)
250 DEF_GVEC_VOP2_32(vcgd)
251 DEF_GVEC_VOP2_32(vclgd)
252 DEF_GVEC_VOP2_64(vcdg)
253 DEF_GVEC_VOP2_64(vcdlg)
254 DEF_GVEC_VOP2_64(vcgd)
255 DEF_GVEC_VOP2_64(vclgd)
256 DEF_GVEC_VOP2(vfi, round_to_int)
257 DEF_GVEC_VOP2(vfsq, sqrt)
258 
259 typedef float32 (*vop32_3_fn)(float32 a, float32 b, float_status *s);
260 static void vop32_3(S390Vector *v1, const S390Vector *v2, const S390Vector *v3,
261                     CPUS390XState *env, bool s, vop32_3_fn fn,
262                     uintptr_t retaddr)
263 {
264     uint8_t vxc, vec_exc = 0;
265     S390Vector tmp = {};
266     int i;
267 
268     for (i = 0; i < 4; i++) {
269         const float32 a = s390_vec_read_float32(v2, i);
270         const float32 b = s390_vec_read_float32(v3, i);
271 
272         s390_vec_write_float32(&tmp, i, fn(a, b, &env->fpu_status));
273         vxc = check_ieee_exc(env, i, false, &vec_exc);
274         if (s || vxc) {
275             break;
276         }
277     }
278     handle_ieee_exc(env, vxc, vec_exc, retaddr);
279     *v1 = tmp;
280 }
281 
282 typedef float64 (*vop64_3_fn)(float64 a, float64 b, float_status *s);
283 static void vop64_3(S390Vector *v1, const S390Vector *v2, const S390Vector *v3,
284                     CPUS390XState *env, bool s, vop64_3_fn fn,
285                     uintptr_t retaddr)
286 {
287     uint8_t vxc, vec_exc = 0;
288     S390Vector tmp = {};
289     int i;
290 
291     for (i = 0; i < 2; i++) {
292         const float64 a = s390_vec_read_float64(v2, i);
293         const float64 b = s390_vec_read_float64(v3, i);
294 
295         s390_vec_write_float64(&tmp, i, fn(a, b, &env->fpu_status));
296         vxc = check_ieee_exc(env, i, false, &vec_exc);
297         if (s || vxc) {
298             break;
299         }
300     }
301     handle_ieee_exc(env, vxc, vec_exc, retaddr);
302     *v1 = tmp;
303 }
304 
305 typedef float128 (*vop128_3_fn)(float128 a, float128 b, float_status *s);
306 static void vop128_3(S390Vector *v1, const S390Vector *v2, const S390Vector *v3,
307                      CPUS390XState *env, bool s, vop128_3_fn fn,
308                      uintptr_t retaddr)
309 {
310     const float128 a = s390_vec_read_float128(v2);
311     const float128 b = s390_vec_read_float128(v3);
312     uint8_t vxc, vec_exc = 0;
313     S390Vector tmp = {};
314 
315     s390_vec_write_float128(&tmp, fn(a, b, &env->fpu_status));
316     vxc = check_ieee_exc(env, 0, false, &vec_exc);
317     handle_ieee_exc(env, vxc, vec_exc, retaddr);
318     *v1 = tmp;
319 }
320 
321 #define DEF_GVEC_VOP3_B(NAME, OP, BITS)                                        \
322 void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, const void *v3,       \
323                               CPUS390XState *env, uint32_t desc)               \
324 {                                                                              \
325     const bool se = extract32(simd_data(desc), 3, 1);                          \
326                                                                                \
327     vop##BITS##_3(v1, v2, v3, env, se, float##BITS##_##OP, GETPC());           \
328 }
329 
330 #define DEF_GVEC_VOP3(NAME, OP)                                                \
331 DEF_GVEC_VOP3_B(NAME, OP, 32)                                                  \
332 DEF_GVEC_VOP3_B(NAME, OP, 64)                                                  \
333 DEF_GVEC_VOP3_B(NAME, OP, 128)
334 
335 DEF_GVEC_VOP3(vfa, add)
336 DEF_GVEC_VOP3(vfs, sub)
337 DEF_GVEC_VOP3(vfd, div)
338 DEF_GVEC_VOP3(vfm, mul)
339 
340 static int wfc32(const S390Vector *v1, const S390Vector *v2,
341                  CPUS390XState *env, bool signal, uintptr_t retaddr)
342 {
343     /* only the zero-indexed elements are compared */
344     const float32 a = s390_vec_read_float32(v1, 0);
345     const float32 b = s390_vec_read_float32(v2, 0);
346     uint8_t vxc, vec_exc = 0;
347     int cmp;
348 
349     if (signal) {
350         cmp = float32_compare(a, b, &env->fpu_status);
351     } else {
352         cmp = float32_compare_quiet(a, b, &env->fpu_status);
353     }
354     vxc = check_ieee_exc(env, 0, false, &vec_exc);
355     handle_ieee_exc(env, vxc, vec_exc, retaddr);
356 
357     return float_comp_to_cc(env, cmp);
358 }
359 
360 static int wfc64(const S390Vector *v1, const S390Vector *v2,
361                  CPUS390XState *env, bool signal, uintptr_t retaddr)
362 {
363     /* only the zero-indexed elements are compared */
364     const float64 a = s390_vec_read_float64(v1, 0);
365     const float64 b = s390_vec_read_float64(v2, 0);
366     uint8_t vxc, vec_exc = 0;
367     int cmp;
368 
369     if (signal) {
370         cmp = float64_compare(a, b, &env->fpu_status);
371     } else {
372         cmp = float64_compare_quiet(a, b, &env->fpu_status);
373     }
374     vxc = check_ieee_exc(env, 0, false, &vec_exc);
375     handle_ieee_exc(env, vxc, vec_exc, retaddr);
376 
377     return float_comp_to_cc(env, cmp);
378 }
379 
380 static int wfc128(const S390Vector *v1, const S390Vector *v2,
381                   CPUS390XState *env, bool signal, uintptr_t retaddr)
382 {
383     /* only the zero-indexed elements are compared */
384     const float128 a = s390_vec_read_float128(v1);
385     const float128 b = s390_vec_read_float128(v2);
386     uint8_t vxc, vec_exc = 0;
387     int cmp;
388 
389     if (signal) {
390         cmp = float128_compare(a, b, &env->fpu_status);
391     } else {
392         cmp = float128_compare_quiet(a, b, &env->fpu_status);
393     }
394     vxc = check_ieee_exc(env, 0, false, &vec_exc);
395     handle_ieee_exc(env, vxc, vec_exc, retaddr);
396 
397     return float_comp_to_cc(env, cmp);
398 }
399 
400 #define DEF_GVEC_WFC_B(NAME, SIGNAL, BITS)                                     \
401 void HELPER(gvec_##NAME##BITS)(const void *v1, const void *v2,                 \
402                                CPUS390XState *env, uint32_t desc)              \
403 {                                                                              \
404     env->cc_op = wfc##BITS(v1, v2, env, SIGNAL, GETPC());                      \
405 }
406 
407 #define DEF_GVEC_WFC(NAME, SIGNAL)                                             \
408      DEF_GVEC_WFC_B(NAME, SIGNAL, 32)                                          \
409      DEF_GVEC_WFC_B(NAME, SIGNAL, 64)                                          \
410      DEF_GVEC_WFC_B(NAME, SIGNAL, 128)
411 
412 DEF_GVEC_WFC(wfc, false)
413 DEF_GVEC_WFC(wfk, true)
414 
415 typedef bool (*vfc32_fn)(float32 a, float32 b, float_status *status);
416 static int vfc32(S390Vector *v1, const S390Vector *v2, const S390Vector *v3,
417                  CPUS390XState *env, bool s, vfc32_fn fn, uintptr_t retaddr)
418 {
419     uint8_t vxc, vec_exc = 0;
420     S390Vector tmp = {};
421     int match = 0;
422     int i;
423 
424     for (i = 0; i < 4; i++) {
425         const float32 a = s390_vec_read_float32(v2, i);
426         const float32 b = s390_vec_read_float32(v3, i);
427 
428         /* swap the order of the parameters, so we can use existing functions */
429         if (fn(b, a, &env->fpu_status)) {
430             match++;
431             s390_vec_write_element32(&tmp, i, -1u);
432         }
433         vxc = check_ieee_exc(env, i, false, &vec_exc);
434         if (s || vxc) {
435             break;
436         }
437     }
438 
439     handle_ieee_exc(env, vxc, vec_exc, retaddr);
440     *v1 = tmp;
441     if (match) {
442         return s || match == 4 ? 0 : 1;
443     }
444     return 3;
445 }
446 
447 typedef bool (*vfc64_fn)(float64 a, float64 b, float_status *status);
448 static int vfc64(S390Vector *v1, const S390Vector *v2, const S390Vector *v3,
449                  CPUS390XState *env, bool s, vfc64_fn fn, uintptr_t retaddr)
450 {
451     uint8_t vxc, vec_exc = 0;
452     S390Vector tmp = {};
453     int match = 0;
454     int i;
455 
456     for (i = 0; i < 2; i++) {
457         const float64 a = s390_vec_read_float64(v2, i);
458         const float64 b = s390_vec_read_float64(v3, i);
459 
460         /* swap the order of the parameters, so we can use existing functions */
461         if (fn(b, a, &env->fpu_status)) {
462             match++;
463             s390_vec_write_element64(&tmp, i, -1ull);
464         }
465         vxc = check_ieee_exc(env, i, false, &vec_exc);
466         if (s || vxc) {
467             break;
468         }
469     }
470 
471     handle_ieee_exc(env, vxc, vec_exc, retaddr);
472     *v1 = tmp;
473     if (match) {
474         return s || match == 2 ? 0 : 1;
475     }
476     return 3;
477 }
478 
479 typedef bool (*vfc128_fn)(float128 a, float128 b, float_status *status);
480 static int vfc128(S390Vector *v1, const S390Vector *v2, const S390Vector *v3,
481                  CPUS390XState *env, bool s, vfc128_fn fn, uintptr_t retaddr)
482 {
483     const float128 a = s390_vec_read_float128(v2);
484     const float128 b = s390_vec_read_float128(v3);
485     uint8_t vxc, vec_exc = 0;
486     S390Vector tmp = {};
487     bool match = false;
488 
489     /* swap the order of the parameters, so we can use existing functions */
490     if (fn(b, a, &env->fpu_status)) {
491         match = true;
492         s390_vec_write_element64(&tmp, 0, -1ull);
493         s390_vec_write_element64(&tmp, 1, -1ull);
494     }
495     vxc = check_ieee_exc(env, 0, false, &vec_exc);
496     handle_ieee_exc(env, vxc, vec_exc, retaddr);
497     *v1 = tmp;
498     return match ? 0 : 3;
499 }
500 
501 #define DEF_GVEC_VFC_B(NAME, OP, BITS)                                         \
502 void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, const void *v3,       \
503                                CPUS390XState *env, uint32_t desc)              \
504 {                                                                              \
505     const bool se = extract32(simd_data(desc), 3, 1);                          \
506     const bool sq = extract32(simd_data(desc), 2, 1);                          \
507     vfc##BITS##_fn fn = sq ? float##BITS##_##OP : float##BITS##_##OP##_quiet;  \
508                                                                                \
509     vfc##BITS(v1, v2, v3, env, se, fn, GETPC());                               \
510 }                                                                              \
511                                                                                \
512 void HELPER(gvec_##NAME##BITS##_cc)(void *v1, const void *v2, const void *v3,  \
513                                     CPUS390XState *env, uint32_t desc)         \
514 {                                                                              \
515     const bool se = extract32(simd_data(desc), 3, 1);                          \
516     const bool sq = extract32(simd_data(desc), 2, 1);                          \
517     vfc##BITS##_fn fn = sq ? float##BITS##_##OP : float##BITS##_##OP##_quiet;  \
518                                                                                \
519     env->cc_op = vfc##BITS(v1, v2, v3, env, se, fn, GETPC());                  \
520 }
521 
522 #define DEF_GVEC_VFC(NAME, OP)                                                 \
523 DEF_GVEC_VFC_B(NAME, OP, 32)                                                   \
524 DEF_GVEC_VFC_B(NAME, OP, 64)                                                   \
525 DEF_GVEC_VFC_B(NAME, OP, 128)                                                  \
526 
527 DEF_GVEC_VFC(vfce, eq)
528 DEF_GVEC_VFC(vfch, lt)
529 DEF_GVEC_VFC(vfche, le)
530 
531 void HELPER(gvec_vfll32)(void *v1, const void *v2, CPUS390XState *env,
532                          uint32_t desc)
533 {
534     const bool s = extract32(simd_data(desc), 3, 1);
535     uint8_t vxc, vec_exc = 0;
536     S390Vector tmp = {};
537     int i;
538 
539     for (i = 0; i < 2; i++) {
540         /* load from even element */
541         const float32 a = s390_vec_read_element32(v2, i * 2);
542         const uint64_t ret = float32_to_float64(a, &env->fpu_status);
543 
544         s390_vec_write_element64(&tmp, i, ret);
545         /* indicate the source element */
546         vxc = check_ieee_exc(env, i * 2, false, &vec_exc);
547         if (s || vxc) {
548             break;
549         }
550     }
551     handle_ieee_exc(env, vxc, vec_exc, GETPC());
552     *(S390Vector *)v1 = tmp;
553 }
554 
555 void HELPER(gvec_vfll64)(void *v1, const void *v2, CPUS390XState *env,
556                          uint32_t desc)
557 {
558     /* load from even element */
559     const float128 ret = float64_to_float128(s390_vec_read_float64(v2, 0),
560                                              &env->fpu_status);
561     uint8_t vxc, vec_exc = 0;
562 
563     vxc = check_ieee_exc(env, 0, false, &vec_exc);
564     handle_ieee_exc(env, vxc, vec_exc, GETPC());
565     s390_vec_write_float128(v1, ret);
566 }
567 
568 void HELPER(gvec_vflr64)(void *v1, const void *v2, CPUS390XState *env,
569                          uint32_t desc)
570 {
571     const uint8_t erm = extract32(simd_data(desc), 4, 4);
572     const bool s = extract32(simd_data(desc), 3, 1);
573     const bool XxC = extract32(simd_data(desc), 2, 1);
574     uint8_t vxc, vec_exc = 0;
575     S390Vector tmp = {};
576     int i, old_mode;
577 
578     old_mode = s390_swap_bfp_rounding_mode(env, erm);
579     for (i = 0; i < 2; i++) {
580         float64 a = s390_vec_read_element64(v2, i);
581         uint32_t ret = float64_to_float32(a, &env->fpu_status);
582 
583         /* place at even element */
584         s390_vec_write_element32(&tmp, i * 2, ret);
585         /* indicate the source element */
586         vxc = check_ieee_exc(env, i, XxC, &vec_exc);
587         if (s || vxc) {
588             break;
589         }
590     }
591     s390_restore_bfp_rounding_mode(env, old_mode);
592     handle_ieee_exc(env, vxc, vec_exc, GETPC());
593     *(S390Vector *)v1 = tmp;
594 }
595 
596 void HELPER(gvec_vflr128)(void *v1, const void *v2, CPUS390XState *env,
597                           uint32_t desc)
598 {
599     const uint8_t erm = extract32(simd_data(desc), 4, 4);
600     const bool XxC = extract32(simd_data(desc), 2, 1);
601     uint8_t vxc, vec_exc = 0;
602     int old_mode;
603     float64 ret;
604 
605     old_mode = s390_swap_bfp_rounding_mode(env, erm);
606     ret = float128_to_float64(s390_vec_read_float128(v2), &env->fpu_status);
607     vxc = check_ieee_exc(env, 0, XxC, &vec_exc);
608     s390_restore_bfp_rounding_mode(env, old_mode);
609     handle_ieee_exc(env, vxc, vec_exc, GETPC());
610 
611     /* place at even element, odd element is unpredictable */
612     s390_vec_write_float64(v1, 0, ret);
613 }
614 
615 static void vfma32(S390Vector *v1, const S390Vector *v2, const S390Vector *v3,
616                    const S390Vector *v4, CPUS390XState *env, bool s, int flags,
617                    uintptr_t retaddr)
618 {
619     uint8_t vxc, vec_exc = 0;
620     S390Vector tmp = {};
621     int i;
622 
623     for (i = 0; i < 4; i++) {
624         const float32 a = s390_vec_read_float32(v2, i);
625         const float32 b = s390_vec_read_float32(v3, i);
626         const float32 c = s390_vec_read_float32(v4, i);
627         float32 ret = float32_muladd(a, b, c, flags, &env->fpu_status);
628 
629         s390_vec_write_float32(&tmp, i, ret);
630         vxc = check_ieee_exc(env, i, false, &vec_exc);
631         if (s || vxc) {
632             break;
633         }
634     }
635     handle_ieee_exc(env, vxc, vec_exc, retaddr);
636     *v1 = tmp;
637 }
638 
639 static void vfma64(S390Vector *v1, const S390Vector *v2, const S390Vector *v3,
640                    const S390Vector *v4, CPUS390XState *env, bool s, int flags,
641                    uintptr_t retaddr)
642 {
643     uint8_t vxc, vec_exc = 0;
644     S390Vector tmp = {};
645     int i;
646 
647     for (i = 0; i < 2; i++) {
648         const float64 a = s390_vec_read_float64(v2, i);
649         const float64 b = s390_vec_read_float64(v3, i);
650         const float64 c = s390_vec_read_float64(v4, i);
651         const float64 ret = float64_muladd(a, b, c, flags, &env->fpu_status);
652 
653         s390_vec_write_float64(&tmp, i, ret);
654         vxc = check_ieee_exc(env, i, false, &vec_exc);
655         if (s || vxc) {
656             break;
657         }
658     }
659     handle_ieee_exc(env, vxc, vec_exc, retaddr);
660     *v1 = tmp;
661 }
662 
663 static void vfma128(S390Vector *v1, const S390Vector *v2, const S390Vector *v3,
664                     const S390Vector *v4, CPUS390XState *env, bool s, int flags,
665                     uintptr_t retaddr)
666 {
667     const float128 a = s390_vec_read_float128(v2);
668     const float128 b = s390_vec_read_float128(v3);
669     const float128 c = s390_vec_read_float128(v4);
670     uint8_t vxc, vec_exc = 0;
671     float128 ret;
672 
673     ret = float128_muladd(a, b, c, flags, &env->fpu_status);
674     vxc = check_ieee_exc(env, 0, false, &vec_exc);
675     handle_ieee_exc(env, vxc, vec_exc, retaddr);
676     s390_vec_write_float128(v1, ret);
677 }
678 
679 #define DEF_GVEC_VFMA_B(NAME, FLAGS, BITS)                                     \
680 void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, const void *v3,       \
681                                const void *v4, CPUS390XState *env,             \
682                                uint32_t desc)                                  \
683 {                                                                              \
684     const bool se = extract32(simd_data(desc), 3, 1);                          \
685                                                                                \
686     vfma##BITS(v1, v2, v3, v4, env, se, FLAGS, GETPC());                       \
687 }
688 
689 #define DEF_GVEC_VFMA(NAME, FLAGS)                                             \
690     DEF_GVEC_VFMA_B(NAME, FLAGS, 32)                                           \
691     DEF_GVEC_VFMA_B(NAME, FLAGS, 64)                                           \
692     DEF_GVEC_VFMA_B(NAME, FLAGS, 128)
693 
694 DEF_GVEC_VFMA(vfma, 0)
695 DEF_GVEC_VFMA(vfms, float_muladd_negate_c)
696 DEF_GVEC_VFMA(vfnma, float_muladd_negate_result)
697 DEF_GVEC_VFMA(vfnms, float_muladd_negate_c | float_muladd_negate_result)
698 
699 void HELPER(gvec_vftci32)(void *v1, const void *v2, CPUS390XState *env,
700                           uint32_t desc)
701 {
702     uint16_t i3 = extract32(simd_data(desc), 4, 12);
703     bool s = extract32(simd_data(desc), 3, 1);
704     int i, match = 0;
705 
706     for (i = 0; i < 4; i++) {
707         float32 a = s390_vec_read_float32(v2, i);
708 
709         if (float32_dcmask(env, a) & i3) {
710             match++;
711             s390_vec_write_element32(v1, i, -1u);
712         } else {
713             s390_vec_write_element32(v1, i, 0);
714         }
715         if (s) {
716             break;
717         }
718     }
719 
720     if (match == 4 || (s && match)) {
721         env->cc_op = 0;
722     } else if (match) {
723         env->cc_op = 1;
724     } else {
725         env->cc_op = 3;
726     }
727 }
728 
729 void HELPER(gvec_vftci64)(void *v1, const void *v2, CPUS390XState *env,
730                           uint32_t desc)
731 {
732     const uint16_t i3 = extract32(simd_data(desc), 4, 12);
733     const bool s = extract32(simd_data(desc), 3, 1);
734     int i, match = 0;
735 
736     for (i = 0; i < 2; i++) {
737         const float64 a = s390_vec_read_float64(v2, i);
738 
739         if (float64_dcmask(env, a) & i3) {
740             match++;
741             s390_vec_write_element64(v1, i, -1ull);
742         } else {
743             s390_vec_write_element64(v1, i, 0);
744         }
745         if (s) {
746             break;
747         }
748     }
749 
750     if (match == 2 || (s && match)) {
751         env->cc_op = 0;
752     } else if (match) {
753         env->cc_op = 1;
754     } else {
755         env->cc_op = 3;
756     }
757 }
758 
759 void HELPER(gvec_vftci128)(void *v1, const void *v2, CPUS390XState *env,
760                            uint32_t desc)
761 {
762     const float128 a = s390_vec_read_float128(v2);
763     uint16_t i3 = extract32(simd_data(desc), 4, 12);
764 
765     if (float128_dcmask(env, a) & i3) {
766         env->cc_op = 0;
767         s390_vec_write_element64(v1, 0, -1ull);
768         s390_vec_write_element64(v1, 1, -1ull);
769     } else {
770         env->cc_op = 3;
771         s390_vec_write_element64(v1, 0, 0);
772         s390_vec_write_element64(v1, 1, 0);
773     }
774 }
775 
776 typedef enum S390MinMaxType {
777     S390_MINMAX_TYPE_IEEE = 0,
778     S390_MINMAX_TYPE_JAVA,
779     S390_MINMAX_TYPE_C_MACRO,
780     S390_MINMAX_TYPE_CPP,
781     S390_MINMAX_TYPE_F,
782 } S390MinMaxType;
783 
784 typedef enum S390MinMaxRes {
785     S390_MINMAX_RES_MINMAX = 0,
786     S390_MINMAX_RES_A,
787     S390_MINMAX_RES_B,
788     S390_MINMAX_RES_SILENCE_A,
789     S390_MINMAX_RES_SILENCE_B,
790 } S390MinMaxRes;
791 
792 static S390MinMaxRes vfmin_res(uint16_t dcmask_a, uint16_t dcmask_b,
793                                S390MinMaxType type, float_status *s)
794 {
795     const bool neg_a = dcmask_a & DCMASK_NEGATIVE;
796     const bool nan_a = dcmask_a & DCMASK_NAN;
797     const bool nan_b = dcmask_b & DCMASK_NAN;
798 
799     g_assert(type > S390_MINMAX_TYPE_IEEE && type <= S390_MINMAX_TYPE_F);
800 
801     if (unlikely((dcmask_a | dcmask_b) & DCMASK_NAN)) {
802         const bool sig_a = dcmask_a & DCMASK_SIGNALING_NAN;
803         const bool sig_b = dcmask_b & DCMASK_SIGNALING_NAN;
804 
805         if ((dcmask_a | dcmask_b) & DCMASK_SIGNALING_NAN) {
806             s->float_exception_flags |= float_flag_invalid;
807         }
808         switch (type) {
809         case S390_MINMAX_TYPE_JAVA:
810             if (sig_a) {
811                 return S390_MINMAX_RES_SILENCE_A;
812             } else if (sig_b) {
813                 return S390_MINMAX_RES_SILENCE_B;
814             }
815             return nan_a ? S390_MINMAX_RES_A : S390_MINMAX_RES_B;
816         case S390_MINMAX_TYPE_F:
817             return nan_b ? S390_MINMAX_RES_A : S390_MINMAX_RES_B;
818         case S390_MINMAX_TYPE_C_MACRO:
819             s->float_exception_flags |= float_flag_invalid;
820             return S390_MINMAX_RES_B;
821         case S390_MINMAX_TYPE_CPP:
822             s->float_exception_flags |= float_flag_invalid;
823             return S390_MINMAX_RES_A;
824         default:
825             g_assert_not_reached();
826         }
827     } else if (unlikely((dcmask_a & DCMASK_ZERO) && (dcmask_b & DCMASK_ZERO))) {
828         switch (type) {
829         case S390_MINMAX_TYPE_JAVA:
830             return neg_a ? S390_MINMAX_RES_A : S390_MINMAX_RES_B;
831         case S390_MINMAX_TYPE_C_MACRO:
832             return S390_MINMAX_RES_B;
833         case S390_MINMAX_TYPE_F:
834             return !neg_a ? S390_MINMAX_RES_B : S390_MINMAX_RES_A;
835         case S390_MINMAX_TYPE_CPP:
836             return S390_MINMAX_RES_A;
837         default:
838             g_assert_not_reached();
839         }
840     }
841     return S390_MINMAX_RES_MINMAX;
842 }
843 
844 static S390MinMaxRes vfmax_res(uint16_t dcmask_a, uint16_t dcmask_b,
845                                S390MinMaxType type, float_status *s)
846 {
847     g_assert(type > S390_MINMAX_TYPE_IEEE && type <= S390_MINMAX_TYPE_F);
848 
849     if (unlikely((dcmask_a | dcmask_b) & DCMASK_NAN)) {
850         const bool sig_a = dcmask_a & DCMASK_SIGNALING_NAN;
851         const bool sig_b = dcmask_b & DCMASK_SIGNALING_NAN;
852         const bool nan_a = dcmask_a & DCMASK_NAN;
853         const bool nan_b = dcmask_b & DCMASK_NAN;
854 
855         if ((dcmask_a | dcmask_b) & DCMASK_SIGNALING_NAN) {
856             s->float_exception_flags |= float_flag_invalid;
857         }
858         switch (type) {
859         case S390_MINMAX_TYPE_JAVA:
860             if (sig_a) {
861                 return S390_MINMAX_RES_SILENCE_A;
862             } else if (sig_b) {
863                 return S390_MINMAX_RES_SILENCE_B;
864             }
865             return nan_a ? S390_MINMAX_RES_A : S390_MINMAX_RES_B;
866         case S390_MINMAX_TYPE_F:
867             return nan_b ? S390_MINMAX_RES_A : S390_MINMAX_RES_B;
868         case S390_MINMAX_TYPE_C_MACRO:
869             s->float_exception_flags |= float_flag_invalid;
870             return S390_MINMAX_RES_B;
871         case S390_MINMAX_TYPE_CPP:
872             s->float_exception_flags |= float_flag_invalid;
873             return S390_MINMAX_RES_A;
874         default:
875             g_assert_not_reached();
876         }
877     } else if (unlikely((dcmask_a & DCMASK_ZERO) && (dcmask_b & DCMASK_ZERO))) {
878         const bool neg_a = dcmask_a & DCMASK_NEGATIVE;
879 
880         switch (type) {
881         case S390_MINMAX_TYPE_JAVA:
882         case S390_MINMAX_TYPE_F:
883             return neg_a ? S390_MINMAX_RES_B : S390_MINMAX_RES_A;
884         case S390_MINMAX_TYPE_C_MACRO:
885             return S390_MINMAX_RES_B;
886         case S390_MINMAX_TYPE_CPP:
887             return S390_MINMAX_RES_A;
888         default:
889             g_assert_not_reached();
890         }
891     }
892     return S390_MINMAX_RES_MINMAX;
893 }
894 
895 static S390MinMaxRes vfminmax_res(uint16_t dcmask_a, uint16_t dcmask_b,
896                                   S390MinMaxType type, bool is_min,
897                                   float_status *s)
898 {
899     return is_min ? vfmin_res(dcmask_a, dcmask_b, type, s) :
900                     vfmax_res(dcmask_a, dcmask_b, type, s);
901 }
902 
903 static void vfminmax32(S390Vector *v1, const S390Vector *v2,
904                        const S390Vector *v3, CPUS390XState *env,
905                        S390MinMaxType type, bool is_min, bool is_abs, bool se,
906                        uintptr_t retaddr)
907 {
908     float_status *s = &env->fpu_status;
909     uint8_t vxc, vec_exc = 0;
910     S390Vector tmp = {};
911     int i;
912 
913     for (i = 0; i < 4; i++) {
914         float32 a = s390_vec_read_float32(v2, i);
915         float32 b = s390_vec_read_float32(v3, i);
916         float32 result;
917 
918         if (type != S390_MINMAX_TYPE_IEEE) {
919             S390MinMaxRes res;
920 
921             if (is_abs) {
922                 a = float32_abs(a);
923                 b = float32_abs(b);
924             }
925 
926             res = vfminmax_res(float32_dcmask(env, a), float32_dcmask(env, b),
927                                type, is_min, s);
928             switch (res) {
929             case S390_MINMAX_RES_MINMAX:
930                 result = is_min ? float32_min(a, b, s) : float32_max(a, b, s);
931                 break;
932             case S390_MINMAX_RES_A:
933                 result = a;
934                 break;
935             case S390_MINMAX_RES_B:
936                 result = b;
937                 break;
938             case S390_MINMAX_RES_SILENCE_A:
939                 result = float32_silence_nan(a, s);
940                 break;
941             case S390_MINMAX_RES_SILENCE_B:
942                 result = float32_silence_nan(b, s);
943                 break;
944             default:
945                 g_assert_not_reached();
946             }
947         } else if (!is_abs) {
948             result = is_min ? float32_minnum(a, b, &env->fpu_status) :
949                               float32_maxnum(a, b, &env->fpu_status);
950         } else {
951             result = is_min ? float32_minnummag(a, b, &env->fpu_status) :
952                               float32_maxnummag(a, b, &env->fpu_status);
953         }
954 
955         s390_vec_write_float32(&tmp, i, result);
956         vxc = check_ieee_exc(env, i, false, &vec_exc);
957         if (se || vxc) {
958             break;
959         }
960     }
961     handle_ieee_exc(env, vxc, vec_exc, retaddr);
962     *v1 = tmp;
963 }
964 
965 static void vfminmax64(S390Vector *v1, const S390Vector *v2,
966                        const S390Vector *v3, CPUS390XState *env,
967                        S390MinMaxType type, bool is_min, bool is_abs, bool se,
968                        uintptr_t retaddr)
969 {
970     float_status *s = &env->fpu_status;
971     uint8_t vxc, vec_exc = 0;
972     S390Vector tmp = {};
973     int i;
974 
975     for (i = 0; i < 2; i++) {
976         float64 a = s390_vec_read_float64(v2, i);
977         float64 b = s390_vec_read_float64(v3, i);
978         float64 result;
979 
980         if (type != S390_MINMAX_TYPE_IEEE) {
981             S390MinMaxRes res;
982 
983             if (is_abs) {
984                 a = float64_abs(a);
985                 b = float64_abs(b);
986             }
987 
988             res = vfminmax_res(float64_dcmask(env, a), float64_dcmask(env, b),
989                                type, is_min, s);
990             switch (res) {
991             case S390_MINMAX_RES_MINMAX:
992                 result = is_min ? float64_min(a, b, s) : float64_max(a, b, s);
993                 break;
994             case S390_MINMAX_RES_A:
995                 result = a;
996                 break;
997             case S390_MINMAX_RES_B:
998                 result = b;
999                 break;
1000             case S390_MINMAX_RES_SILENCE_A:
1001                 result = float64_silence_nan(a, s);
1002                 break;
1003             case S390_MINMAX_RES_SILENCE_B:
1004                 result = float64_silence_nan(b, s);
1005                 break;
1006             default:
1007                 g_assert_not_reached();
1008             }
1009         } else if (!is_abs) {
1010             result = is_min ? float64_minnum(a, b, &env->fpu_status) :
1011                               float64_maxnum(a, b, &env->fpu_status);
1012         } else {
1013             result = is_min ? float64_minnummag(a, b, &env->fpu_status) :
1014                               float64_maxnummag(a, b, &env->fpu_status);
1015         }
1016 
1017         s390_vec_write_float64(&tmp, i, result);
1018         vxc = check_ieee_exc(env, i, false, &vec_exc);
1019         if (se || vxc) {
1020             break;
1021         }
1022     }
1023     handle_ieee_exc(env, vxc, vec_exc, retaddr);
1024     *v1 = tmp;
1025 }
1026 
1027 static void vfminmax128(S390Vector *v1, const S390Vector *v2,
1028                         const S390Vector *v3, CPUS390XState *env,
1029                         S390MinMaxType type, bool is_min, bool is_abs, bool se,
1030                         uintptr_t retaddr)
1031 {
1032     float128 a = s390_vec_read_float128(v2);
1033     float128 b = s390_vec_read_float128(v3);
1034     float_status *s = &env->fpu_status;
1035     uint8_t vxc, vec_exc = 0;
1036     float128 result;
1037 
1038     if (type != S390_MINMAX_TYPE_IEEE) {
1039         S390MinMaxRes res;
1040 
1041         if (is_abs) {
1042             a = float128_abs(a);
1043             b = float128_abs(b);
1044         }
1045 
1046         res = vfminmax_res(float128_dcmask(env, a), float128_dcmask(env, b),
1047                            type, is_min, s);
1048         switch (res) {
1049         case S390_MINMAX_RES_MINMAX:
1050             result = is_min ? float128_min(a, b, s) : float128_max(a, b, s);
1051             break;
1052         case S390_MINMAX_RES_A:
1053             result = a;
1054             break;
1055         case S390_MINMAX_RES_B:
1056             result = b;
1057             break;
1058         case S390_MINMAX_RES_SILENCE_A:
1059             result = float128_silence_nan(a, s);
1060             break;
1061         case S390_MINMAX_RES_SILENCE_B:
1062             result = float128_silence_nan(b, s);
1063             break;
1064         default:
1065             g_assert_not_reached();
1066         }
1067     } else if (!is_abs) {
1068         result = is_min ? float128_minnum(a, b, &env->fpu_status) :
1069                           float128_maxnum(a, b, &env->fpu_status);
1070     } else {
1071         result = is_min ? float128_minnummag(a, b, &env->fpu_status) :
1072                           float128_maxnummag(a, b, &env->fpu_status);
1073     }
1074 
1075     vxc = check_ieee_exc(env, 0, false, &vec_exc);
1076     handle_ieee_exc(env, vxc, vec_exc, retaddr);
1077     s390_vec_write_float128(v1, result);
1078 }
1079 
1080 #define DEF_GVEC_VFMINMAX_B(NAME, IS_MIN, BITS)                                \
1081 void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, const void *v3,       \
1082                                CPUS390XState *env, uint32_t desc)              \
1083 {                                                                              \
1084     const bool se = extract32(simd_data(desc), 3, 1);                          \
1085     uint8_t type = extract32(simd_data(desc), 4, 4);                           \
1086     bool is_abs = false;                                                       \
1087                                                                                \
1088     if (type >= 8) {                                                           \
1089         is_abs = true;                                                         \
1090         type -= 8;                                                             \
1091     }                                                                          \
1092                                                                                \
1093     vfminmax##BITS(v1, v2, v3, env, type, IS_MIN, is_abs, se, GETPC());        \
1094 }
1095 
1096 #define DEF_GVEC_VFMINMAX(NAME, IS_MIN)                                        \
1097     DEF_GVEC_VFMINMAX_B(NAME, IS_MIN, 32)                                      \
1098     DEF_GVEC_VFMINMAX_B(NAME, IS_MIN, 64)                                      \
1099     DEF_GVEC_VFMINMAX_B(NAME, IS_MIN, 128)
1100 
1101 DEF_GVEC_VFMINMAX(vfmax, false)
1102 DEF_GVEC_VFMINMAX(vfmin, true)
1103