]>
Commit | Line | Data |
---|---|---|
3a0eae85 DH |
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" | |
3a0eae85 | 13 | #include "cpu.h" |
b6b47223 | 14 | #include "s390x-internal.h" |
3a0eae85 DH |
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 | ||
0987961d DH |
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 | ||
863b9507 DH |
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 | ||
0987961d DH |
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 | ||
863b9507 DH |
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 | ||
0987961d DH |
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 | ||
acb269a4 DH |
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 | ||
21bd6ea2 | 136 | typedef float64 (*vop64_2_fn)(float64 a, float_status *s); |
bb03fd84 DH |
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++) { | |
21bd6ea2 | 147 | const float64 a = s390_vec_read_float64(v2, i); |
bb03fd84 | 148 | |
21bd6ea2 | 149 | s390_vec_write_float64(&tmp, i, fn(a, &env->fpu_status)); |
bb03fd84 DH |
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 | ||
acb269a4 DH |
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 | ||
21bd6ea2 DH |
178 | static float64 vcdg64(float64 a, float_status *s) |
179 | { | |
180 | return int64_to_float64(a, s); | |
181 | } | |
182 | ||
183 | static float64 vcdlg64(float64 a, float_status *s) | |
184 | { | |
185 | return uint64_to_float64(a, s); | |
186 | } | |
187 | ||
188 | static float64 vcgd64(float64 a, float_status *s) | |
189 | { | |
190 | const float64 tmp = float64_to_int64(a, s); | |
191 | ||
192 | return float64_is_any_nan(a) ? INT64_MIN : tmp; | |
193 | } | |
194 | ||
195 | static float64 vclgd64(float64 a, float_status *s) | |
196 | { | |
197 | const float64 tmp = float64_to_uint64(a, s); | |
198 | ||
199 | return float64_is_any_nan(a) ? 0 : tmp; | |
200 | } | |
201 | ||
202 | #define DEF_GVEC_VOP2_FN(NAME, FN, BITS) \ | |
203 | void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, CPUS390XState *env, \ | |
204 | uint32_t desc) \ | |
205 | { \ | |
206 | const uint8_t erm = extract32(simd_data(desc), 4, 4); \ | |
207 | const bool se = extract32(simd_data(desc), 3, 1); \ | |
208 | const bool XxC = extract32(simd_data(desc), 2, 1); \ | |
209 | \ | |
210 | vop##BITS##_2(v1, v2, env, se, XxC, erm, FN, GETPC()); \ | |
211 | } | |
212 | ||
213 | #define DEF_GVEC_VOP2_64(NAME) \ | |
214 | DEF_GVEC_VOP2_FN(NAME, NAME##64, 64) | |
215 | ||
216 | #define DEF_GVEC_VOP2(NAME, OP) \ | |
acb269a4 DH |
217 | DEF_GVEC_VOP2_FN(NAME, float32_##OP, 32) \ |
218 | DEF_GVEC_VOP2_FN(NAME, float64_##OP, 64) \ | |
219 | DEF_GVEC_VOP2_FN(NAME, float128_##OP, 128) | |
21bd6ea2 DH |
220 | |
221 | DEF_GVEC_VOP2_64(vcdg) | |
222 | DEF_GVEC_VOP2_64(vcdlg) | |
223 | DEF_GVEC_VOP2_64(vcgd) | |
224 | DEF_GVEC_VOP2_64(vclgd) | |
225 | DEF_GVEC_VOP2(vfi, round_to_int) | |
226 | DEF_GVEC_VOP2(vfsq, sqrt) | |
227 | ||
0987961d DH |
228 | typedef float32 (*vop32_3_fn)(float32 a, float32 b, float_status *s); |
229 | static void vop32_3(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, | |
230 | CPUS390XState *env, bool s, vop32_3_fn fn, | |
231 | uintptr_t retaddr) | |
232 | { | |
233 | uint8_t vxc, vec_exc = 0; | |
234 | S390Vector tmp = {}; | |
235 | int i; | |
236 | ||
237 | for (i = 0; i < 4; i++) { | |
238 | const float32 a = s390_vec_read_float32(v2, i); | |
239 | const float32 b = s390_vec_read_float32(v3, i); | |
240 | ||
241 | s390_vec_write_float32(&tmp, i, fn(a, b, &env->fpu_status)); | |
242 | vxc = check_ieee_exc(env, i, false, &vec_exc); | |
243 | if (s || vxc) { | |
244 | break; | |
245 | } | |
246 | } | |
247 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
248 | *v1 = tmp; | |
249 | } | |
250 | ||
863b9507 | 251 | typedef float64 (*vop64_3_fn)(float64 a, float64 b, float_status *s); |
3a0eae85 DH |
252 | static void vop64_3(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, |
253 | CPUS390XState *env, bool s, vop64_3_fn fn, | |
254 | uintptr_t retaddr) | |
255 | { | |
256 | uint8_t vxc, vec_exc = 0; | |
257 | S390Vector tmp = {}; | |
258 | int i; | |
259 | ||
260 | for (i = 0; i < 2; i++) { | |
863b9507 DH |
261 | const float64 a = s390_vec_read_float64(v2, i); |
262 | const float64 b = s390_vec_read_float64(v3, i); | |
3a0eae85 | 263 | |
863b9507 | 264 | s390_vec_write_float64(&tmp, i, fn(a, b, &env->fpu_status)); |
3a0eae85 DH |
265 | vxc = check_ieee_exc(env, i, false, &vec_exc); |
266 | if (s || vxc) { | |
267 | break; | |
268 | } | |
269 | } | |
270 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
271 | *v1 = tmp; | |
272 | } | |
273 | ||
0987961d DH |
274 | typedef float128 (*vop128_3_fn)(float128 a, float128 b, float_status *s); |
275 | static void vop128_3(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, | |
276 | CPUS390XState *env, bool s, vop128_3_fn fn, | |
277 | uintptr_t retaddr) | |
278 | { | |
279 | const float128 a = s390_vec_read_float128(v2); | |
280 | const float128 b = s390_vec_read_float128(v3); | |
281 | uint8_t vxc, vec_exc = 0; | |
282 | S390Vector tmp = {}; | |
283 | ||
284 | s390_vec_write_float128(&tmp, fn(a, b, &env->fpu_status)); | |
285 | vxc = check_ieee_exc(env, 0, false, &vec_exc); | |
286 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
287 | *v1 = tmp; | |
288 | } | |
289 | ||
290 | #define DEF_GVEC_VOP3_B(NAME, OP, BITS) \ | |
291 | void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, const void *v3, \ | |
292 | CPUS390XState *env, uint32_t desc) \ | |
863b9507 DH |
293 | { \ |
294 | const bool se = extract32(simd_data(desc), 3, 1); \ | |
295 | \ | |
0987961d | 296 | vop##BITS##_3(v1, v2, v3, env, se, float##BITS##_##OP, GETPC()); \ |
3a0eae85 DH |
297 | } |
298 | ||
0987961d DH |
299 | #define DEF_GVEC_VOP3(NAME, OP) \ |
300 | DEF_GVEC_VOP3_B(NAME, OP, 32) \ | |
301 | DEF_GVEC_VOP3_B(NAME, OP, 64) \ | |
302 | DEF_GVEC_VOP3_B(NAME, OP, 128) | |
303 | ||
863b9507 DH |
304 | DEF_GVEC_VOP3(vfa, add) |
305 | DEF_GVEC_VOP3(vfs, sub) | |
306 | DEF_GVEC_VOP3(vfd, div) | |
307 | DEF_GVEC_VOP3(vfm, mul) | |
5b89f0fb | 308 | |
1c6b5b47 DH |
309 | static int wfc32(const S390Vector *v1, const S390Vector *v2, |
310 | CPUS390XState *env, bool signal, uintptr_t retaddr) | |
311 | { | |
312 | /* only the zero-indexed elements are compared */ | |
313 | const float32 a = s390_vec_read_float32(v1, 0); | |
314 | const float32 b = s390_vec_read_float32(v2, 0); | |
315 | uint8_t vxc, vec_exc = 0; | |
316 | int cmp; | |
317 | ||
318 | if (signal) { | |
319 | cmp = float32_compare(a, b, &env->fpu_status); | |
320 | } else { | |
321 | cmp = float32_compare_quiet(a, b, &env->fpu_status); | |
322 | } | |
323 | vxc = check_ieee_exc(env, 0, false, &vec_exc); | |
324 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
325 | ||
326 | return float_comp_to_cc(env, cmp); | |
327 | } | |
328 | ||
5b89f0fb DH |
329 | static int wfc64(const S390Vector *v1, const S390Vector *v2, |
330 | CPUS390XState *env, bool signal, uintptr_t retaddr) | |
331 | { | |
332 | /* only the zero-indexed elements are compared */ | |
4da79375 DH |
333 | const float64 a = s390_vec_read_float64(v1, 0); |
334 | const float64 b = s390_vec_read_float64(v2, 0); | |
5b89f0fb DH |
335 | uint8_t vxc, vec_exc = 0; |
336 | int cmp; | |
337 | ||
338 | if (signal) { | |
339 | cmp = float64_compare(a, b, &env->fpu_status); | |
340 | } else { | |
341 | cmp = float64_compare_quiet(a, b, &env->fpu_status); | |
342 | } | |
343 | vxc = check_ieee_exc(env, 0, false, &vec_exc); | |
344 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
345 | ||
346 | return float_comp_to_cc(env, cmp); | |
347 | } | |
348 | ||
1c6b5b47 DH |
349 | static int wfc128(const S390Vector *v1, const S390Vector *v2, |
350 | CPUS390XState *env, bool signal, uintptr_t retaddr) | |
351 | { | |
352 | /* only the zero-indexed elements are compared */ | |
353 | const float128 a = s390_vec_read_float128(v1); | |
354 | const float128 b = s390_vec_read_float128(v2); | |
355 | uint8_t vxc, vec_exc = 0; | |
356 | int cmp; | |
357 | ||
358 | if (signal) { | |
359 | cmp = float128_compare(a, b, &env->fpu_status); | |
360 | } else { | |
361 | cmp = float128_compare_quiet(a, b, &env->fpu_status); | |
362 | } | |
363 | vxc = check_ieee_exc(env, 0, false, &vec_exc); | |
364 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
365 | ||
366 | return float_comp_to_cc(env, cmp); | |
367 | } | |
368 | ||
4da79375 DH |
369 | #define DEF_GVEC_WFC_B(NAME, SIGNAL, BITS) \ |
370 | void HELPER(gvec_##NAME##BITS)(const void *v1, const void *v2, \ | |
371 | CPUS390XState *env, uint32_t desc) \ | |
372 | { \ | |
373 | env->cc_op = wfc##BITS(v1, v2, env, SIGNAL, GETPC()); \ | |
5b89f0fb DH |
374 | } |
375 | ||
4da79375 | 376 | #define DEF_GVEC_WFC(NAME, SIGNAL) \ |
1c6b5b47 DH |
377 | DEF_GVEC_WFC_B(NAME, SIGNAL, 32) \ |
378 | DEF_GVEC_WFC_B(NAME, SIGNAL, 64) \ | |
379 | DEF_GVEC_WFC_B(NAME, SIGNAL, 128) | |
4da79375 DH |
380 | |
381 | DEF_GVEC_WFC(wfc, false) | |
382 | DEF_GVEC_WFC(wfk, true) | |
2c806ab4 | 383 | |
e384332c DH |
384 | typedef bool (*vfc32_fn)(float32 a, float32 b, float_status *status); |
385 | static int vfc32(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, | |
386 | CPUS390XState *env, bool s, vfc32_fn fn, uintptr_t retaddr) | |
387 | { | |
388 | uint8_t vxc, vec_exc = 0; | |
389 | S390Vector tmp = {}; | |
390 | int match = 0; | |
391 | int i; | |
392 | ||
393 | for (i = 0; i < 4; i++) { | |
394 | const float32 a = s390_vec_read_float32(v2, i); | |
395 | const float32 b = s390_vec_read_float32(v3, i); | |
396 | ||
397 | /* swap the order of the parameters, so we can use existing functions */ | |
398 | if (fn(b, a, &env->fpu_status)) { | |
399 | match++; | |
400 | s390_vec_write_element32(&tmp, i, -1u); | |
401 | } | |
402 | vxc = check_ieee_exc(env, i, false, &vec_exc); | |
403 | if (s || vxc) { | |
404 | break; | |
405 | } | |
406 | } | |
407 | ||
408 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
409 | *v1 = tmp; | |
410 | if (match) { | |
411 | return s || match == 4 ? 0 : 1; | |
412 | } | |
413 | return 3; | |
414 | } | |
415 | ||
0673ecdf | 416 | typedef bool (*vfc64_fn)(float64 a, float64 b, float_status *status); |
2c806ab4 DH |
417 | static int vfc64(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, |
418 | CPUS390XState *env, bool s, vfc64_fn fn, uintptr_t retaddr) | |
419 | { | |
420 | uint8_t vxc, vec_exc = 0; | |
421 | S390Vector tmp = {}; | |
422 | int match = 0; | |
423 | int i; | |
424 | ||
425 | for (i = 0; i < 2; i++) { | |
64deb65a DH |
426 | const float64 a = s390_vec_read_float64(v2, i); |
427 | const float64 b = s390_vec_read_float64(v3, i); | |
2c806ab4 DH |
428 | |
429 | /* swap the order of the parameters, so we can use existing functions */ | |
430 | if (fn(b, a, &env->fpu_status)) { | |
431 | match++; | |
432 | s390_vec_write_element64(&tmp, i, -1ull); | |
433 | } | |
434 | vxc = check_ieee_exc(env, i, false, &vec_exc); | |
435 | if (s || vxc) { | |
436 | break; | |
437 | } | |
438 | } | |
439 | ||
440 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
441 | *v1 = tmp; | |
442 | if (match) { | |
443 | return s || match == 2 ? 0 : 1; | |
444 | } | |
445 | return 3; | |
446 | } | |
447 | ||
e384332c DH |
448 | typedef bool (*vfc128_fn)(float128 a, float128 b, float_status *status); |
449 | static int vfc128(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, | |
450 | CPUS390XState *env, bool s, vfc128_fn fn, uintptr_t retaddr) | |
451 | { | |
452 | const float128 a = s390_vec_read_float128(v2); | |
453 | const float128 b = s390_vec_read_float128(v3); | |
454 | uint8_t vxc, vec_exc = 0; | |
455 | S390Vector tmp = {}; | |
456 | bool match = false; | |
457 | ||
458 | /* swap the order of the parameters, so we can use existing functions */ | |
459 | if (fn(b, a, &env->fpu_status)) { | |
460 | match = true; | |
461 | s390_vec_write_element64(&tmp, 0, -1ull); | |
462 | s390_vec_write_element64(&tmp, 1, -1ull); | |
463 | } | |
464 | vxc = check_ieee_exc(env, 0, false, &vec_exc); | |
465 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
466 | *v1 = tmp; | |
467 | return match ? 0 : 3; | |
468 | } | |
469 | ||
64deb65a DH |
470 | #define DEF_GVEC_VFC_B(NAME, OP, BITS) \ |
471 | void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, const void *v3, \ | |
472 | CPUS390XState *env, uint32_t desc) \ | |
473 | { \ | |
474 | const bool se = extract32(simd_data(desc), 3, 1); \ | |
e384332c DH |
475 | const bool sq = extract32(simd_data(desc), 2, 1); \ |
476 | vfc##BITS##_fn fn = sq ? float##BITS##_##OP : float##BITS##_##OP##_quiet; \ | |
64deb65a DH |
477 | \ |
478 | vfc##BITS(v1, v2, v3, env, se, fn, GETPC()); \ | |
479 | } \ | |
480 | \ | |
481 | void HELPER(gvec_##NAME##BITS##_cc)(void *v1, const void *v2, const void *v3, \ | |
482 | CPUS390XState *env, uint32_t desc) \ | |
483 | { \ | |
484 | const bool se = extract32(simd_data(desc), 3, 1); \ | |
e384332c DH |
485 | const bool sq = extract32(simd_data(desc), 2, 1); \ |
486 | vfc##BITS##_fn fn = sq ? float##BITS##_##OP : float##BITS##_##OP##_quiet; \ | |
64deb65a DH |
487 | \ |
488 | env->cc_op = vfc##BITS(v1, v2, v3, env, se, fn, GETPC()); \ | |
2c806ab4 DH |
489 | } |
490 | ||
64deb65a | 491 | #define DEF_GVEC_VFC(NAME, OP) \ |
e384332c DH |
492 | DEF_GVEC_VFC_B(NAME, OP, 32) \ |
493 | DEF_GVEC_VFC_B(NAME, OP, 64) \ | |
494 | DEF_GVEC_VFC_B(NAME, OP, 128) \ | |
2c806ab4 | 495 | |
64deb65a DH |
496 | DEF_GVEC_VFC(vfce, eq) |
497 | DEF_GVEC_VFC(vfch, lt) | |
498 | DEF_GVEC_VFC(vfche, le) | |
bb03fd84 | 499 | |
860b707b DH |
500 | void HELPER(gvec_vfll32)(void *v1, const void *v2, CPUS390XState *env, |
501 | uint32_t desc) | |
1a76e59d | 502 | { |
860b707b | 503 | const bool s = extract32(simd_data(desc), 3, 1); |
1a76e59d DH |
504 | uint8_t vxc, vec_exc = 0; |
505 | S390Vector tmp = {}; | |
506 | int i; | |
507 | ||
508 | for (i = 0; i < 2; i++) { | |
509 | /* load from even element */ | |
510 | const float32 a = s390_vec_read_element32(v2, i * 2); | |
511 | const uint64_t ret = float32_to_float64(a, &env->fpu_status); | |
512 | ||
513 | s390_vec_write_element64(&tmp, i, ret); | |
514 | /* indicate the source element */ | |
515 | vxc = check_ieee_exc(env, i * 2, false, &vec_exc); | |
516 | if (s || vxc) { | |
517 | break; | |
518 | } | |
519 | } | |
860b707b DH |
520 | handle_ieee_exc(env, vxc, vec_exc, GETPC()); |
521 | *(S390Vector *)v1 = tmp; | |
1a76e59d | 522 | } |
4500ede4 | 523 | |
2e96005e DH |
524 | void HELPER(gvec_vfll64)(void *v1, const void *v2, CPUS390XState *env, |
525 | uint32_t desc) | |
526 | { | |
527 | /* load from even element */ | |
528 | const float128 ret = float64_to_float128(s390_vec_read_float64(v2, 0), | |
529 | &env->fpu_status); | |
530 | uint8_t vxc, vec_exc = 0; | |
531 | ||
532 | vxc = check_ieee_exc(env, 0, false, &vec_exc); | |
533 | handle_ieee_exc(env, vxc, vec_exc, GETPC()); | |
534 | s390_vec_write_float128(v1, ret); | |
535 | } | |
536 | ||
977e43d9 DH |
537 | void HELPER(gvec_vflr64)(void *v1, const void *v2, CPUS390XState *env, |
538 | uint32_t desc) | |
4500ede4 | 539 | { |
977e43d9 DH |
540 | const uint8_t erm = extract32(simd_data(desc), 4, 4); |
541 | const bool s = extract32(simd_data(desc), 3, 1); | |
542 | const bool XxC = extract32(simd_data(desc), 2, 1); | |
4500ede4 DH |
543 | uint8_t vxc, vec_exc = 0; |
544 | S390Vector tmp = {}; | |
545 | int i, old_mode; | |
546 | ||
547 | old_mode = s390_swap_bfp_rounding_mode(env, erm); | |
548 | for (i = 0; i < 2; i++) { | |
549 | float64 a = s390_vec_read_element64(v2, i); | |
550 | uint32_t ret = float64_to_float32(a, &env->fpu_status); | |
551 | ||
552 | /* place at even element */ | |
553 | s390_vec_write_element32(&tmp, i * 2, ret); | |
554 | /* indicate the source element */ | |
555 | vxc = check_ieee_exc(env, i, XxC, &vec_exc); | |
556 | if (s || vxc) { | |
557 | break; | |
558 | } | |
559 | } | |
560 | s390_restore_bfp_rounding_mode(env, old_mode); | |
977e43d9 DH |
561 | handle_ieee_exc(env, vxc, vec_exc, GETPC()); |
562 | *(S390Vector *)v1 = tmp; | |
4500ede4 | 563 | } |
8d47d4d2 | 564 | |
9cbc8be0 DH |
565 | void HELPER(gvec_vflr128)(void *v1, const void *v2, CPUS390XState *env, |
566 | uint32_t desc) | |
567 | { | |
568 | const uint8_t erm = extract32(simd_data(desc), 4, 4); | |
569 | const bool XxC = extract32(simd_data(desc), 2, 1); | |
570 | uint8_t vxc, vec_exc = 0; | |
571 | int old_mode; | |
572 | float64 ret; | |
573 | ||
574 | old_mode = s390_swap_bfp_rounding_mode(env, erm); | |
575 | ret = float128_to_float64(s390_vec_read_float128(v2), &env->fpu_status); | |
576 | vxc = check_ieee_exc(env, 0, XxC, &vec_exc); | |
577 | s390_restore_bfp_rounding_mode(env, old_mode); | |
578 | handle_ieee_exc(env, vxc, vec_exc, GETPC()); | |
579 | ||
580 | /* place at even element, odd element is unpredictable */ | |
581 | s390_vec_write_float64(v1, 0, ret); | |
582 | } | |
583 | ||
e257abc8 DH |
584 | static void vfma32(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, |
585 | const S390Vector *v4, CPUS390XState *env, bool s, int flags, | |
586 | uintptr_t retaddr) | |
587 | { | |
588 | uint8_t vxc, vec_exc = 0; | |
589 | S390Vector tmp = {}; | |
590 | int i; | |
591 | ||
592 | for (i = 0; i < 4; i++) { | |
593 | const float32 a = s390_vec_read_float32(v2, i); | |
594 | const float32 b = s390_vec_read_float32(v3, i); | |
595 | const float32 c = s390_vec_read_float32(v4, i); | |
596 | float32 ret = float32_muladd(a, b, c, flags, &env->fpu_status); | |
597 | ||
598 | s390_vec_write_float32(&tmp, i, ret); | |
599 | vxc = check_ieee_exc(env, i, false, &vec_exc); | |
600 | if (s || vxc) { | |
601 | break; | |
602 | } | |
603 | } | |
604 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
605 | *v1 = tmp; | |
606 | } | |
607 | ||
c64c5984 DH |
608 | static void vfma64(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, |
609 | const S390Vector *v4, CPUS390XState *env, bool s, int flags, | |
610 | uintptr_t retaddr) | |
611 | { | |
612 | uint8_t vxc, vec_exc = 0; | |
613 | S390Vector tmp = {}; | |
614 | int i; | |
615 | ||
616 | for (i = 0; i < 2; i++) { | |
34142ffd DH |
617 | const float64 a = s390_vec_read_float64(v2, i); |
618 | const float64 b = s390_vec_read_float64(v3, i); | |
619 | const float64 c = s390_vec_read_float64(v4, i); | |
620 | const float64 ret = float64_muladd(a, b, c, flags, &env->fpu_status); | |
c64c5984 | 621 | |
34142ffd | 622 | s390_vec_write_float64(&tmp, i, ret); |
c64c5984 DH |
623 | vxc = check_ieee_exc(env, i, false, &vec_exc); |
624 | if (s || vxc) { | |
625 | break; | |
626 | } | |
627 | } | |
628 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
629 | *v1 = tmp; | |
630 | } | |
631 | ||
e257abc8 DH |
632 | static void vfma128(S390Vector *v1, const S390Vector *v2, const S390Vector *v3, |
633 | const S390Vector *v4, CPUS390XState *env, bool s, int flags, | |
634 | uintptr_t retaddr) | |
635 | { | |
636 | const float128 a = s390_vec_read_float128(v2); | |
637 | const float128 b = s390_vec_read_float128(v3); | |
638 | const float128 c = s390_vec_read_float128(v4); | |
639 | uint8_t vxc, vec_exc = 0; | |
640 | float128 ret; | |
641 | ||
642 | ret = float128_muladd(a, b, c, flags, &env->fpu_status); | |
643 | vxc = check_ieee_exc(env, 0, false, &vec_exc); | |
644 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
645 | s390_vec_write_float128(v1, ret); | |
646 | } | |
647 | ||
34142ffd DH |
648 | #define DEF_GVEC_VFMA_B(NAME, FLAGS, BITS) \ |
649 | void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, const void *v3, \ | |
650 | const void *v4, CPUS390XState *env, \ | |
651 | uint32_t desc) \ | |
652 | { \ | |
653 | const bool se = extract32(simd_data(desc), 3, 1); \ | |
654 | \ | |
655 | vfma##BITS(v1, v2, v3, v4, env, se, FLAGS, GETPC()); \ | |
c64c5984 DH |
656 | } |
657 | ||
34142ffd | 658 | #define DEF_GVEC_VFMA(NAME, FLAGS) \ |
e257abc8 DH |
659 | DEF_GVEC_VFMA_B(NAME, FLAGS, 32) \ |
660 | DEF_GVEC_VFMA_B(NAME, FLAGS, 64) \ | |
661 | DEF_GVEC_VFMA_B(NAME, FLAGS, 128) | |
c64c5984 | 662 | |
34142ffd DH |
663 | DEF_GVEC_VFMA(vfma, 0) |
664 | DEF_GVEC_VFMA(vfms, float_muladd_negate_c) | |
f0249730 DH |
665 | DEF_GVEC_VFMA(vfnma, float_muladd_negate_result) |
666 | DEF_GVEC_VFMA(vfnms, float_muladd_negate_c | float_muladd_negate_result) | |
5938f20c | 667 | |
a38b5a0e DH |
668 | void HELPER(gvec_vftci32)(void *v1, const void *v2, CPUS390XState *env, |
669 | uint32_t desc) | |
670 | { | |
671 | uint16_t i3 = extract32(simd_data(desc), 4, 12); | |
672 | bool s = extract32(simd_data(desc), 3, 1); | |
673 | int i, match = 0; | |
674 | ||
675 | for (i = 0; i < 4; i++) { | |
676 | float32 a = s390_vec_read_float32(v2, i); | |
677 | ||
678 | if (float32_dcmask(env, a) & i3) { | |
679 | match++; | |
680 | s390_vec_write_element32(v1, i, -1u); | |
681 | } else { | |
682 | s390_vec_write_element32(v1, i, 0); | |
683 | } | |
684 | if (s) { | |
685 | break; | |
686 | } | |
687 | } | |
688 | ||
689 | if (match == 4 || (s && match)) { | |
690 | env->cc_op = 0; | |
691 | } else if (match) { | |
692 | env->cc_op = 1; | |
693 | } else { | |
694 | env->cc_op = 3; | |
695 | } | |
696 | } | |
697 | ||
622ebe64 DH |
698 | void HELPER(gvec_vftci64)(void *v1, const void *v2, CPUS390XState *env, |
699 | uint32_t desc) | |
83b955f9 | 700 | { |
622ebe64 DH |
701 | const uint16_t i3 = extract32(simd_data(desc), 4, 12); |
702 | const bool s = extract32(simd_data(desc), 3, 1); | |
83b955f9 DH |
703 | int i, match = 0; |
704 | ||
705 | for (i = 0; i < 2; i++) { | |
622ebe64 | 706 | const float64 a = s390_vec_read_float64(v2, i); |
83b955f9 DH |
707 | |
708 | if (float64_dcmask(env, a) & i3) { | |
709 | match++; | |
710 | s390_vec_write_element64(v1, i, -1ull); | |
711 | } else { | |
712 | s390_vec_write_element64(v1, i, 0); | |
713 | } | |
714 | if (s) { | |
715 | break; | |
716 | } | |
717 | } | |
718 | ||
622ebe64 DH |
719 | if (match == 2 || (s && match)) { |
720 | env->cc_op = 0; | |
721 | } else if (match) { | |
722 | env->cc_op = 1; | |
723 | } else { | |
724 | env->cc_op = 3; | |
83b955f9 | 725 | } |
83b955f9 | 726 | } |
a38b5a0e DH |
727 | |
728 | void HELPER(gvec_vftci128)(void *v1, const void *v2, CPUS390XState *env, | |
729 | uint32_t desc) | |
730 | { | |
731 | const float128 a = s390_vec_read_float128(v2); | |
732 | uint16_t i3 = extract32(simd_data(desc), 4, 12); | |
733 | ||
734 | if (float128_dcmask(env, a) & i3) { | |
735 | env->cc_op = 0; | |
736 | s390_vec_write_element64(v1, 0, -1ull); | |
737 | s390_vec_write_element64(v1, 1, -1ull); | |
738 | } else { | |
739 | env->cc_op = 3; | |
740 | s390_vec_write_element64(v1, 0, 0); | |
741 | s390_vec_write_element64(v1, 1, 0); | |
742 | } | |
743 | } | |
da480752 DH |
744 | |
745 | typedef enum S390MinMaxType { | |
746 | S390_MINMAX_TYPE_IEEE = 0, | |
747 | S390_MINMAX_TYPE_JAVA, | |
748 | S390_MINMAX_TYPE_C_MACRO, | |
749 | S390_MINMAX_TYPE_CPP, | |
750 | S390_MINMAX_TYPE_F, | |
751 | } S390MinMaxType; | |
752 | ||
753 | typedef enum S390MinMaxRes { | |
754 | S390_MINMAX_RES_MINMAX = 0, | |
755 | S390_MINMAX_RES_A, | |
756 | S390_MINMAX_RES_B, | |
757 | S390_MINMAX_RES_SILENCE_A, | |
758 | S390_MINMAX_RES_SILENCE_B, | |
759 | } S390MinMaxRes; | |
760 | ||
761 | static S390MinMaxRes vfmin_res(uint16_t dcmask_a, uint16_t dcmask_b, | |
762 | S390MinMaxType type, float_status *s) | |
763 | { | |
764 | const bool neg_a = dcmask_a & DCMASK_NEGATIVE; | |
765 | const bool nan_a = dcmask_a & DCMASK_NAN; | |
766 | const bool nan_b = dcmask_b & DCMASK_NAN; | |
767 | ||
768 | g_assert(type > S390_MINMAX_TYPE_IEEE && type <= S390_MINMAX_TYPE_F); | |
769 | ||
770 | if (unlikely((dcmask_a | dcmask_b) & DCMASK_NAN)) { | |
771 | const bool sig_a = dcmask_a & DCMASK_SIGNALING_NAN; | |
772 | const bool sig_b = dcmask_b & DCMASK_SIGNALING_NAN; | |
773 | ||
774 | if ((dcmask_a | dcmask_b) & DCMASK_SIGNALING_NAN) { | |
775 | s->float_exception_flags |= float_flag_invalid; | |
776 | } | |
777 | switch (type) { | |
778 | case S390_MINMAX_TYPE_JAVA: | |
779 | if (sig_a) { | |
780 | return S390_MINMAX_RES_SILENCE_A; | |
781 | } else if (sig_b) { | |
782 | return S390_MINMAX_RES_SILENCE_B; | |
783 | } | |
784 | return nan_a ? S390_MINMAX_RES_A : S390_MINMAX_RES_B; | |
785 | case S390_MINMAX_TYPE_F: | |
786 | return nan_b ? S390_MINMAX_RES_A : S390_MINMAX_RES_B; | |
787 | case S390_MINMAX_TYPE_C_MACRO: | |
788 | s->float_exception_flags |= float_flag_invalid; | |
789 | return S390_MINMAX_RES_B; | |
790 | case S390_MINMAX_TYPE_CPP: | |
791 | s->float_exception_flags |= float_flag_invalid; | |
792 | return S390_MINMAX_RES_A; | |
793 | default: | |
794 | g_assert_not_reached(); | |
795 | } | |
796 | } else if (unlikely(dcmask_a & dcmask_b & DCMASK_ZERO)) { | |
797 | switch (type) { | |
798 | case S390_MINMAX_TYPE_JAVA: | |
799 | return neg_a ? S390_MINMAX_RES_A : S390_MINMAX_RES_B; | |
800 | case S390_MINMAX_TYPE_C_MACRO: | |
801 | return S390_MINMAX_RES_B; | |
802 | case S390_MINMAX_TYPE_F: | |
803 | return !neg_a ? S390_MINMAX_RES_B : S390_MINMAX_RES_A; | |
804 | case S390_MINMAX_TYPE_CPP: | |
805 | return S390_MINMAX_RES_A; | |
806 | default: | |
807 | g_assert_not_reached(); | |
808 | } | |
809 | } | |
810 | return S390_MINMAX_RES_MINMAX; | |
811 | } | |
812 | ||
813 | static S390MinMaxRes vfmax_res(uint16_t dcmask_a, uint16_t dcmask_b, | |
814 | S390MinMaxType type, float_status *s) | |
815 | { | |
816 | g_assert(type > S390_MINMAX_TYPE_IEEE && type <= S390_MINMAX_TYPE_F); | |
817 | ||
818 | if (unlikely((dcmask_a | dcmask_b) & DCMASK_NAN)) { | |
819 | const bool sig_a = dcmask_a & DCMASK_SIGNALING_NAN; | |
820 | const bool sig_b = dcmask_b & DCMASK_SIGNALING_NAN; | |
821 | const bool nan_a = dcmask_a & DCMASK_NAN; | |
822 | const bool nan_b = dcmask_b & DCMASK_NAN; | |
823 | ||
824 | if ((dcmask_a | dcmask_b) & DCMASK_SIGNALING_NAN) { | |
825 | s->float_exception_flags |= float_flag_invalid; | |
826 | } | |
827 | switch (type) { | |
828 | case S390_MINMAX_TYPE_JAVA: | |
829 | if (sig_a) { | |
830 | return S390_MINMAX_RES_SILENCE_A; | |
831 | } else if (sig_b) { | |
832 | return S390_MINMAX_RES_SILENCE_B; | |
833 | } | |
834 | return nan_a ? S390_MINMAX_RES_A : S390_MINMAX_RES_B; | |
835 | case S390_MINMAX_TYPE_F: | |
836 | return nan_b ? S390_MINMAX_RES_A : S390_MINMAX_RES_B; | |
837 | case S390_MINMAX_TYPE_C_MACRO: | |
838 | s->float_exception_flags |= float_flag_invalid; | |
839 | return S390_MINMAX_RES_B; | |
840 | case S390_MINMAX_TYPE_CPP: | |
841 | s->float_exception_flags |= float_flag_invalid; | |
842 | return S390_MINMAX_RES_A; | |
843 | default: | |
844 | g_assert_not_reached(); | |
845 | } | |
846 | } else if (unlikely(dcmask_a & dcmask_b & DCMASK_ZERO)) { | |
847 | const bool neg_a = dcmask_a & DCMASK_NEGATIVE; | |
848 | ||
849 | switch (type) { | |
850 | case S390_MINMAX_TYPE_JAVA: | |
851 | case S390_MINMAX_TYPE_F: | |
852 | return neg_a ? S390_MINMAX_RES_B : S390_MINMAX_RES_A; | |
853 | case S390_MINMAX_TYPE_C_MACRO: | |
854 | return S390_MINMAX_RES_B; | |
855 | case S390_MINMAX_TYPE_CPP: | |
856 | return S390_MINMAX_RES_A; | |
857 | default: | |
858 | g_assert_not_reached(); | |
859 | } | |
860 | } | |
861 | return S390_MINMAX_RES_MINMAX; | |
862 | } | |
863 | ||
864 | static S390MinMaxRes vfminmax_res(uint16_t dcmask_a, uint16_t dcmask_b, | |
865 | S390MinMaxType type, bool is_min, | |
866 | float_status *s) | |
867 | { | |
868 | return is_min ? vfmin_res(dcmask_a, dcmask_b, type, s) : | |
869 | vfmax_res(dcmask_a, dcmask_b, type, s); | |
870 | } | |
871 | ||
872 | static void vfminmax32(S390Vector *v1, const S390Vector *v2, | |
873 | const S390Vector *v3, CPUS390XState *env, | |
874 | S390MinMaxType type, bool is_min, bool is_abs, bool se, | |
875 | uintptr_t retaddr) | |
876 | { | |
877 | float_status *s = &env->fpu_status; | |
878 | uint8_t vxc, vec_exc = 0; | |
879 | S390Vector tmp = {}; | |
880 | int i; | |
881 | ||
882 | for (i = 0; i < 4; i++) { | |
883 | float32 a = s390_vec_read_float32(v2, i); | |
884 | float32 b = s390_vec_read_float32(v3, i); | |
885 | float32 result; | |
886 | ||
887 | if (type != S390_MINMAX_TYPE_IEEE) { | |
888 | S390MinMaxRes res; | |
889 | ||
890 | if (is_abs) { | |
891 | a = float32_abs(a); | |
892 | b = float32_abs(b); | |
893 | } | |
894 | ||
895 | res = vfminmax_res(float32_dcmask(env, a), float32_dcmask(env, b), | |
896 | type, is_min, s); | |
897 | switch (res) { | |
898 | case S390_MINMAX_RES_MINMAX: | |
899 | result = is_min ? float32_min(a, b, s) : float32_max(a, b, s); | |
900 | break; | |
901 | case S390_MINMAX_RES_A: | |
902 | result = a; | |
903 | break; | |
904 | case S390_MINMAX_RES_B: | |
905 | result = b; | |
906 | break; | |
907 | case S390_MINMAX_RES_SILENCE_A: | |
908 | result = float32_silence_nan(a, s); | |
909 | break; | |
910 | case S390_MINMAX_RES_SILENCE_B: | |
911 | result = float32_silence_nan(b, s); | |
912 | break; | |
913 | default: | |
914 | g_assert_not_reached(); | |
915 | } | |
916 | } else if (!is_abs) { | |
917 | result = is_min ? float32_minnum(a, b, &env->fpu_status) : | |
918 | float32_maxnum(a, b, &env->fpu_status); | |
919 | } else { | |
920 | result = is_min ? float32_minnummag(a, b, &env->fpu_status) : | |
921 | float32_maxnummag(a, b, &env->fpu_status); | |
922 | } | |
923 | ||
924 | s390_vec_write_float32(&tmp, i, result); | |
925 | vxc = check_ieee_exc(env, i, false, &vec_exc); | |
926 | if (se || vxc) { | |
927 | break; | |
928 | } | |
929 | } | |
930 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
931 | *v1 = tmp; | |
932 | } | |
933 | ||
934 | static void vfminmax64(S390Vector *v1, const S390Vector *v2, | |
935 | const S390Vector *v3, CPUS390XState *env, | |
936 | S390MinMaxType type, bool is_min, bool is_abs, bool se, | |
937 | uintptr_t retaddr) | |
938 | { | |
939 | float_status *s = &env->fpu_status; | |
940 | uint8_t vxc, vec_exc = 0; | |
941 | S390Vector tmp = {}; | |
942 | int i; | |
943 | ||
944 | for (i = 0; i < 2; i++) { | |
945 | float64 a = s390_vec_read_float64(v2, i); | |
946 | float64 b = s390_vec_read_float64(v3, i); | |
947 | float64 result; | |
948 | ||
949 | if (type != S390_MINMAX_TYPE_IEEE) { | |
950 | S390MinMaxRes res; | |
951 | ||
952 | if (is_abs) { | |
953 | a = float64_abs(a); | |
954 | b = float64_abs(b); | |
955 | } | |
956 | ||
957 | res = vfminmax_res(float64_dcmask(env, a), float64_dcmask(env, b), | |
958 | type, is_min, s); | |
959 | switch (res) { | |
960 | case S390_MINMAX_RES_MINMAX: | |
961 | result = is_min ? float64_min(a, b, s) : float64_max(a, b, s); | |
962 | break; | |
963 | case S390_MINMAX_RES_A: | |
964 | result = a; | |
965 | break; | |
966 | case S390_MINMAX_RES_B: | |
967 | result = b; | |
968 | break; | |
969 | case S390_MINMAX_RES_SILENCE_A: | |
970 | result = float64_silence_nan(a, s); | |
971 | break; | |
972 | case S390_MINMAX_RES_SILENCE_B: | |
973 | result = float64_silence_nan(b, s); | |
974 | break; | |
975 | default: | |
976 | g_assert_not_reached(); | |
977 | } | |
978 | } else if (!is_abs) { | |
979 | result = is_min ? float64_minnum(a, b, &env->fpu_status) : | |
980 | float64_maxnum(a, b, &env->fpu_status); | |
981 | } else { | |
982 | result = is_min ? float64_minnummag(a, b, &env->fpu_status) : | |
983 | float64_maxnummag(a, b, &env->fpu_status); | |
984 | } | |
985 | ||
986 | s390_vec_write_float64(&tmp, i, result); | |
987 | vxc = check_ieee_exc(env, i, false, &vec_exc); | |
988 | if (se || vxc) { | |
989 | break; | |
990 | } | |
991 | } | |
992 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
993 | *v1 = tmp; | |
994 | } | |
995 | ||
996 | static void vfminmax128(S390Vector *v1, const S390Vector *v2, | |
997 | const S390Vector *v3, CPUS390XState *env, | |
998 | S390MinMaxType type, bool is_min, bool is_abs, bool se, | |
999 | uintptr_t retaddr) | |
1000 | { | |
1001 | float128 a = s390_vec_read_float128(v2); | |
1002 | float128 b = s390_vec_read_float128(v3); | |
1003 | float_status *s = &env->fpu_status; | |
1004 | uint8_t vxc, vec_exc = 0; | |
1005 | float128 result; | |
1006 | ||
1007 | if (type != S390_MINMAX_TYPE_IEEE) { | |
1008 | S390MinMaxRes res; | |
1009 | ||
1010 | if (is_abs) { | |
1011 | a = float128_abs(a); | |
1012 | b = float128_abs(b); | |
1013 | } | |
1014 | ||
1015 | res = vfminmax_res(float128_dcmask(env, a), float128_dcmask(env, b), | |
1016 | type, is_min, s); | |
1017 | switch (res) { | |
1018 | case S390_MINMAX_RES_MINMAX: | |
1019 | result = is_min ? float128_min(a, b, s) : float128_max(a, b, s); | |
1020 | break; | |
1021 | case S390_MINMAX_RES_A: | |
1022 | result = a; | |
1023 | break; | |
1024 | case S390_MINMAX_RES_B: | |
1025 | result = b; | |
1026 | break; | |
1027 | case S390_MINMAX_RES_SILENCE_A: | |
1028 | result = float128_silence_nan(a, s); | |
1029 | break; | |
1030 | case S390_MINMAX_RES_SILENCE_B: | |
1031 | result = float128_silence_nan(b, s); | |
1032 | break; | |
1033 | default: | |
1034 | g_assert_not_reached(); | |
1035 | } | |
1036 | } else if (!is_abs) { | |
1037 | result = is_min ? float128_minnum(a, b, &env->fpu_status) : | |
1038 | float128_maxnum(a, b, &env->fpu_status); | |
1039 | } else { | |
1040 | result = is_min ? float128_minnummag(a, b, &env->fpu_status) : | |
1041 | float128_maxnummag(a, b, &env->fpu_status); | |
1042 | } | |
1043 | ||
1044 | vxc = check_ieee_exc(env, 0, false, &vec_exc); | |
1045 | handle_ieee_exc(env, vxc, vec_exc, retaddr); | |
1046 | s390_vec_write_float128(v1, result); | |
1047 | } | |
1048 | ||
1049 | #define DEF_GVEC_VFMINMAX_B(NAME, IS_MIN, BITS) \ | |
1050 | void HELPER(gvec_##NAME##BITS)(void *v1, const void *v2, const void *v3, \ | |
1051 | CPUS390XState *env, uint32_t desc) \ | |
1052 | { \ | |
1053 | const bool se = extract32(simd_data(desc), 3, 1); \ | |
1054 | uint8_t type = extract32(simd_data(desc), 4, 4); \ | |
1055 | bool is_abs = false; \ | |
1056 | \ | |
1057 | if (type >= 8) { \ | |
1058 | is_abs = true; \ | |
1059 | type -= 8; \ | |
1060 | } \ | |
1061 | \ | |
1062 | vfminmax##BITS(v1, v2, v3, env, type, IS_MIN, is_abs, se, GETPC()); \ | |
1063 | } | |
1064 | ||
1065 | #define DEF_GVEC_VFMINMAX(NAME, IS_MIN) \ | |
1066 | DEF_GVEC_VFMINMAX_B(NAME, IS_MIN, 32) \ | |
1067 | DEF_GVEC_VFMINMAX_B(NAME, IS_MIN, 64) \ | |
1068 | DEF_GVEC_VFMINMAX_B(NAME, IS_MIN, 128) | |
1069 | ||
1070 | DEF_GVEC_VFMINMAX(vfmax, false) | |
1071 | DEF_GVEC_VFMINMAX(vfmin, true) |