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 "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, int 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 | static inline uint8_t round_from_m34(uint32_t m34) |
172 | { |
173 | return extract32(m34, 0, 4); |
174 | } |
175 | |
176 | static inline bool xxc_from_m34(uint32_t m34) |
177 | { |
178 | /* XxC is bit 1 of m4 */ |
179 | return extract32(m34, 4 + 3 - 1, 1); |
180 | } |
181 | |
182 | /* 32-bit FP addition */ |
183 | uint64_t HELPER(aeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
184 | { |
185 | float32 ret = float32_add(f1, f2, &env->fpu_status); |
186 | handle_exceptions(env, false, GETPC()); |
187 | return ret; |
188 | } |
189 | |
190 | /* 64-bit FP addition */ |
191 | uint64_t HELPER(adb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
192 | { |
193 | float64 ret = float64_add(f1, f2, &env->fpu_status); |
194 | handle_exceptions(env, false, GETPC()); |
195 | return ret; |
196 | } |
197 | |
198 | /* 128-bit FP addition */ |
199 | uint64_t HELPER(axb)(CPUS390XState *env, uint64_t ah, uint64_t al, |
200 | uint64_t bh, uint64_t bl) |
201 | { |
202 | float128 ret = float128_add(make_float128(ah, al), |
203 | make_float128(bh, bl), |
204 | &env->fpu_status); |
205 | handle_exceptions(env, false, GETPC()); |
206 | return RET128(ret); |
207 | } |
208 | |
209 | /* 32-bit FP subtraction */ |
210 | uint64_t HELPER(seb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
211 | { |
212 | float32 ret = float32_sub(f1, f2, &env->fpu_status); |
213 | handle_exceptions(env, false, GETPC()); |
214 | return ret; |
215 | } |
216 | |
217 | /* 64-bit FP subtraction */ |
218 | uint64_t HELPER(sdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
219 | { |
220 | float64 ret = float64_sub(f1, f2, &env->fpu_status); |
221 | handle_exceptions(env, false, GETPC()); |
222 | return ret; |
223 | } |
224 | |
225 | /* 128-bit FP subtraction */ |
226 | uint64_t HELPER(sxb)(CPUS390XState *env, uint64_t ah, uint64_t al, |
227 | uint64_t bh, uint64_t bl) |
228 | { |
229 | float128 ret = float128_sub(make_float128(ah, al), |
230 | make_float128(bh, bl), |
231 | &env->fpu_status); |
232 | handle_exceptions(env, false, GETPC()); |
233 | return RET128(ret); |
234 | } |
235 | |
236 | /* 32-bit FP division */ |
237 | uint64_t HELPER(deb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
238 | { |
239 | float32 ret = float32_div(f1, f2, &env->fpu_status); |
240 | handle_exceptions(env, false, GETPC()); |
241 | return ret; |
242 | } |
243 | |
244 | /* 64-bit FP division */ |
245 | uint64_t HELPER(ddb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
246 | { |
247 | float64 ret = float64_div(f1, f2, &env->fpu_status); |
248 | handle_exceptions(env, false, GETPC()); |
249 | return ret; |
250 | } |
251 | |
252 | /* 128-bit FP division */ |
253 | uint64_t HELPER(dxb)(CPUS390XState *env, uint64_t ah, uint64_t al, |
254 | uint64_t bh, uint64_t bl) |
255 | { |
256 | float128 ret = float128_div(make_float128(ah, al), |
257 | make_float128(bh, bl), |
258 | &env->fpu_status); |
259 | handle_exceptions(env, false, GETPC()); |
260 | return RET128(ret); |
261 | } |
262 | |
263 | /* 32-bit FP multiplication */ |
264 | uint64_t HELPER(meeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
265 | { |
266 | float32 ret = float32_mul(f1, f2, &env->fpu_status); |
267 | handle_exceptions(env, false, GETPC()); |
268 | return ret; |
269 | } |
270 | |
271 | /* 64-bit FP multiplication */ |
272 | uint64_t HELPER(mdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
273 | { |
274 | float64 ret = float64_mul(f1, f2, &env->fpu_status); |
275 | handle_exceptions(env, false, GETPC()); |
276 | return ret; |
277 | } |
278 | |
279 | /* 64/32-bit FP multiplication */ |
280 | uint64_t HELPER(mdeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
281 | { |
282 | float64 ret = float32_to_float64(f2, &env->fpu_status); |
283 | ret = float64_mul(f1, ret, &env->fpu_status); |
284 | handle_exceptions(env, false, GETPC()); |
285 | return ret; |
286 | } |
287 | |
288 | /* 128-bit FP multiplication */ |
289 | uint64_t HELPER(mxb)(CPUS390XState *env, uint64_t ah, uint64_t al, |
290 | uint64_t bh, uint64_t bl) |
291 | { |
292 | float128 ret = float128_mul(make_float128(ah, al), |
293 | make_float128(bh, bl), |
294 | &env->fpu_status); |
295 | handle_exceptions(env, false, GETPC()); |
296 | return RET128(ret); |
297 | } |
298 | |
299 | /* 128/64-bit FP multiplication */ |
300 | uint64_t HELPER(mxdb)(CPUS390XState *env, uint64_t ah, uint64_t al, |
301 | uint64_t f2) |
302 | { |
303 | float128 ret = float64_to_float128(f2, &env->fpu_status); |
304 | ret = float128_mul(make_float128(ah, al), ret, &env->fpu_status); |
305 | handle_exceptions(env, false, GETPC()); |
306 | return RET128(ret); |
307 | } |
308 | |
309 | /* convert 32-bit float to 64-bit float */ |
310 | uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2) |
311 | { |
312 | float64 ret = float32_to_float64(f2, &env->fpu_status); |
313 | handle_exceptions(env, false, GETPC()); |
314 | return ret; |
315 | } |
316 | |
317 | /* convert 128-bit float to 64-bit float */ |
318 | uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al, |
319 | uint32_t m34) |
320 | { |
321 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
322 | float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status); |
323 | |
324 | s390_restore_bfp_rounding_mode(env, old_mode); |
325 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
326 | return ret; |
327 | } |
328 | |
329 | /* convert 64-bit float to 128-bit float */ |
330 | uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2) |
331 | { |
332 | float128 ret = float64_to_float128(f2, &env->fpu_status); |
333 | handle_exceptions(env, false, GETPC()); |
334 | return RET128(ret); |
335 | } |
336 | |
337 | /* convert 32-bit float to 128-bit float */ |
338 | uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2) |
339 | { |
340 | float128 ret = float32_to_float128(f2, &env->fpu_status); |
341 | handle_exceptions(env, false, GETPC()); |
342 | return RET128(ret); |
343 | } |
344 | |
345 | /* convert 64-bit float to 32-bit float */ |
346 | uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2, uint32_t m34) |
347 | { |
348 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
349 | float32 ret = float64_to_float32(f2, &env->fpu_status); |
350 | |
351 | s390_restore_bfp_rounding_mode(env, old_mode); |
352 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
353 | return ret; |
354 | } |
355 | |
356 | /* convert 128-bit float to 32-bit float */ |
357 | uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al, |
358 | uint32_t m34) |
359 | { |
360 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
361 | float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status); |
362 | |
363 | s390_restore_bfp_rounding_mode(env, old_mode); |
364 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
365 | return ret; |
366 | } |
367 | |
368 | /* 32-bit FP compare */ |
369 | uint32_t HELPER(ceb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
370 | { |
371 | int cmp = float32_compare_quiet(f1, f2, &env->fpu_status); |
372 | handle_exceptions(env, false, GETPC()); |
373 | return float_comp_to_cc(env, cmp); |
374 | } |
375 | |
376 | /* 64-bit FP compare */ |
377 | uint32_t HELPER(cdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
378 | { |
379 | int cmp = float64_compare_quiet(f1, f2, &env->fpu_status); |
380 | handle_exceptions(env, false, GETPC()); |
381 | return float_comp_to_cc(env, cmp); |
382 | } |
383 | |
384 | /* 128-bit FP compare */ |
385 | uint32_t HELPER(cxb)(CPUS390XState *env, uint64_t ah, uint64_t al, |
386 | uint64_t bh, uint64_t bl) |
387 | { |
388 | int cmp = float128_compare_quiet(make_float128(ah, al), |
389 | make_float128(bh, bl), |
390 | &env->fpu_status); |
391 | handle_exceptions(env, false, GETPC()); |
392 | return float_comp_to_cc(env, cmp); |
393 | } |
394 | |
395 | int s390_swap_bfp_rounding_mode(CPUS390XState *env, int m3) |
396 | { |
397 | int ret = env->fpu_status.float_rounding_mode; |
398 | |
399 | switch (m3) { |
400 | case 0: |
401 | /* current mode */ |
402 | break; |
403 | case 1: |
404 | /* round to nearest with ties away from 0 */ |
405 | set_float_rounding_mode(float_round_ties_away, &env->fpu_status); |
406 | break; |
407 | case 3: |
408 | /* round to prepare for shorter precision */ |
409 | set_float_rounding_mode(float_round_to_odd, &env->fpu_status); |
410 | break; |
411 | case 4: |
412 | /* round to nearest with ties to even */ |
413 | set_float_rounding_mode(float_round_nearest_even, &env->fpu_status); |
414 | break; |
415 | case 5: |
416 | /* round to zero */ |
417 | set_float_rounding_mode(float_round_to_zero, &env->fpu_status); |
418 | break; |
419 | case 6: |
420 | /* round to +inf */ |
421 | set_float_rounding_mode(float_round_up, &env->fpu_status); |
422 | break; |
423 | case 7: |
424 | /* round to -inf */ |
425 | set_float_rounding_mode(float_round_down, &env->fpu_status); |
426 | break; |
427 | default: |
428 | g_assert_not_reached(); |
429 | } |
430 | return ret; |
431 | } |
432 | |
433 | void s390_restore_bfp_rounding_mode(CPUS390XState *env, int old_mode) |
434 | { |
435 | set_float_rounding_mode(old_mode, &env->fpu_status); |
436 | } |
437 | |
438 | /* convert 64-bit int to 32-bit float */ |
439 | uint64_t HELPER(cegb)(CPUS390XState *env, int64_t v2, uint32_t m34) |
440 | { |
441 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
442 | float32 ret = int64_to_float32(v2, &env->fpu_status); |
443 | |
444 | s390_restore_bfp_rounding_mode(env, old_mode); |
445 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
446 | return ret; |
447 | } |
448 | |
449 | /* convert 64-bit int to 64-bit float */ |
450 | uint64_t HELPER(cdgb)(CPUS390XState *env, int64_t v2, uint32_t m34) |
451 | { |
452 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
453 | float64 ret = int64_to_float64(v2, &env->fpu_status); |
454 | |
455 | s390_restore_bfp_rounding_mode(env, old_mode); |
456 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
457 | return ret; |
458 | } |
459 | |
460 | /* convert 64-bit int to 128-bit float */ |
461 | uint64_t HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m34) |
462 | { |
463 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
464 | float128 ret = int64_to_float128(v2, &env->fpu_status); |
465 | |
466 | s390_restore_bfp_rounding_mode(env, old_mode); |
467 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
468 | return RET128(ret); |
469 | } |
470 | |
471 | /* convert 64-bit uint to 32-bit float */ |
472 | uint64_t HELPER(celgb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
473 | { |
474 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
475 | float32 ret = uint64_to_float32(v2, &env->fpu_status); |
476 | |
477 | s390_restore_bfp_rounding_mode(env, old_mode); |
478 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
479 | return ret; |
480 | } |
481 | |
482 | /* convert 64-bit uint to 64-bit float */ |
483 | uint64_t HELPER(cdlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
484 | { |
485 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
486 | float64 ret = uint64_to_float64(v2, &env->fpu_status); |
487 | |
488 | s390_restore_bfp_rounding_mode(env, old_mode); |
489 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
490 | return ret; |
491 | } |
492 | |
493 | /* convert 64-bit uint to 128-bit float */ |
494 | uint64_t HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
495 | { |
496 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
497 | float128 ret = uint64_to_float128(v2, &env->fpu_status); |
498 | |
499 | s390_restore_bfp_rounding_mode(env, old_mode); |
500 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
501 | return RET128(ret); |
502 | } |
503 | |
504 | /* convert 32-bit float to 64-bit int */ |
505 | uint64_t HELPER(cgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
506 | { |
507 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
508 | int64_t ret = float32_to_int64(v2, &env->fpu_status); |
509 | |
510 | s390_restore_bfp_rounding_mode(env, old_mode); |
511 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
512 | return ret; |
513 | } |
514 | |
515 | /* convert 64-bit float to 64-bit int */ |
516 | uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
517 | { |
518 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
519 | int64_t ret = float64_to_int64(v2, &env->fpu_status); |
520 | |
521 | s390_restore_bfp_rounding_mode(env, old_mode); |
522 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
523 | return ret; |
524 | } |
525 | |
526 | /* convert 128-bit float to 64-bit int */ |
527 | uint64_t HELPER(cgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) |
528 | { |
529 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
530 | float128 v2 = make_float128(h, l); |
531 | int64_t ret = float128_to_int64(v2, &env->fpu_status); |
532 | |
533 | s390_restore_bfp_rounding_mode(env, old_mode); |
534 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
535 | return ret; |
536 | } |
537 | |
538 | /* convert 32-bit float to 32-bit int */ |
539 | uint64_t HELPER(cfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
540 | { |
541 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
542 | int32_t ret = float32_to_int32(v2, &env->fpu_status); |
543 | |
544 | s390_restore_bfp_rounding_mode(env, old_mode); |
545 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
546 | return ret; |
547 | } |
548 | |
549 | /* convert 64-bit float to 32-bit int */ |
550 | uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
551 | { |
552 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
553 | int32_t ret = float64_to_int32(v2, &env->fpu_status); |
554 | |
555 | s390_restore_bfp_rounding_mode(env, old_mode); |
556 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
557 | return ret; |
558 | } |
559 | |
560 | /* convert 128-bit float to 32-bit int */ |
561 | uint64_t HELPER(cfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) |
562 | { |
563 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
564 | float128 v2 = make_float128(h, l); |
565 | int32_t ret = float128_to_int32(v2, &env->fpu_status); |
566 | |
567 | s390_restore_bfp_rounding_mode(env, old_mode); |
568 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
569 | return ret; |
570 | } |
571 | |
572 | /* convert 32-bit float to 64-bit uint */ |
573 | uint64_t HELPER(clgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
574 | { |
575 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
576 | uint64_t ret; |
577 | |
578 | v2 = float32_to_float64(v2, &env->fpu_status); |
579 | ret = float64_to_uint64(v2, &env->fpu_status); |
580 | s390_restore_bfp_rounding_mode(env, old_mode); |
581 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
582 | return ret; |
583 | } |
584 | |
585 | /* convert 64-bit float to 64-bit uint */ |
586 | uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
587 | { |
588 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
589 | uint64_t ret = float64_to_uint64(v2, &env->fpu_status); |
590 | |
591 | s390_restore_bfp_rounding_mode(env, old_mode); |
592 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
593 | return ret; |
594 | } |
595 | |
596 | /* convert 128-bit float to 64-bit uint */ |
597 | uint64_t HELPER(clgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) |
598 | { |
599 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
600 | uint64_t ret = float128_to_uint64(make_float128(h, l), &env->fpu_status); |
601 | |
602 | s390_restore_bfp_rounding_mode(env, old_mode); |
603 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
604 | return ret; |
605 | } |
606 | |
607 | /* convert 32-bit float to 32-bit uint */ |
608 | uint64_t HELPER(clfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
609 | { |
610 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
611 | uint32_t ret = float32_to_uint32(v2, &env->fpu_status); |
612 | |
613 | s390_restore_bfp_rounding_mode(env, old_mode); |
614 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
615 | return ret; |
616 | } |
617 | |
618 | /* convert 64-bit float to 32-bit uint */ |
619 | uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) |
620 | { |
621 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
622 | uint32_t ret = float64_to_uint32(v2, &env->fpu_status); |
623 | |
624 | s390_restore_bfp_rounding_mode(env, old_mode); |
625 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
626 | return ret; |
627 | } |
628 | |
629 | /* convert 128-bit float to 32-bit uint */ |
630 | uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) |
631 | { |
632 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
633 | uint32_t ret = float128_to_uint32(make_float128(h, l), &env->fpu_status); |
634 | |
635 | s390_restore_bfp_rounding_mode(env, old_mode); |
636 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
637 | return ret; |
638 | } |
639 | |
640 | /* round to integer 32-bit */ |
641 | uint64_t HELPER(fieb)(CPUS390XState *env, uint64_t f2, uint32_t m34) |
642 | { |
643 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
644 | float32 ret = float32_round_to_int(f2, &env->fpu_status); |
645 | |
646 | s390_restore_bfp_rounding_mode(env, old_mode); |
647 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
648 | return ret; |
649 | } |
650 | |
651 | /* round to integer 64-bit */ |
652 | uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m34) |
653 | { |
654 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
655 | float64 ret = float64_round_to_int(f2, &env->fpu_status); |
656 | |
657 | s390_restore_bfp_rounding_mode(env, old_mode); |
658 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
659 | return ret; |
660 | } |
661 | |
662 | /* round to integer 128-bit */ |
663 | uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, |
664 | uint32_t m34) |
665 | { |
666 | int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); |
667 | float128 ret = float128_round_to_int(make_float128(ah, al), |
668 | &env->fpu_status); |
669 | |
670 | s390_restore_bfp_rounding_mode(env, old_mode); |
671 | handle_exceptions(env, xxc_from_m34(m34), GETPC()); |
672 | return RET128(ret); |
673 | } |
674 | |
675 | /* 32-bit FP compare and signal */ |
676 | uint32_t HELPER(keb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
677 | { |
678 | int cmp = float32_compare(f1, f2, &env->fpu_status); |
679 | handle_exceptions(env, false, GETPC()); |
680 | return float_comp_to_cc(env, cmp); |
681 | } |
682 | |
683 | /* 64-bit FP compare and signal */ |
684 | uint32_t HELPER(kdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) |
685 | { |
686 | int cmp = float64_compare(f1, f2, &env->fpu_status); |
687 | handle_exceptions(env, false, GETPC()); |
688 | return float_comp_to_cc(env, cmp); |
689 | } |
690 | |
691 | /* 128-bit FP compare and signal */ |
692 | uint32_t HELPER(kxb)(CPUS390XState *env, uint64_t ah, uint64_t al, |
693 | uint64_t bh, uint64_t bl) |
694 | { |
695 | int cmp = float128_compare(make_float128(ah, al), |
696 | make_float128(bh, bl), |
697 | &env->fpu_status); |
698 | handle_exceptions(env, false, GETPC()); |
699 | return float_comp_to_cc(env, cmp); |
700 | } |
701 | |
702 | /* 32-bit FP multiply and add */ |
703 | uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1, |
704 | uint64_t f2, uint64_t f3) |
705 | { |
706 | float32 ret = float32_muladd(f2, f3, f1, 0, &env->fpu_status); |
707 | handle_exceptions(env, false, GETPC()); |
708 | return ret; |
709 | } |
710 | |
711 | /* 64-bit FP multiply and add */ |
712 | uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1, |
713 | uint64_t f2, uint64_t f3) |
714 | { |
715 | float64 ret = float64_muladd(f2, f3, f1, 0, &env->fpu_status); |
716 | handle_exceptions(env, false, GETPC()); |
717 | return ret; |
718 | } |
719 | |
720 | /* 32-bit FP multiply and subtract */ |
721 | uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1, |
722 | uint64_t f2, uint64_t f3) |
723 | { |
724 | float32 ret = float32_muladd(f2, f3, f1, float_muladd_negate_c, |
725 | &env->fpu_status); |
726 | handle_exceptions(env, false, GETPC()); |
727 | return ret; |
728 | } |
729 | |
730 | /* 64-bit FP multiply and subtract */ |
731 | uint64_t HELPER(msdb)(CPUS390XState *env, uint64_t f1, |
732 | uint64_t f2, uint64_t f3) |
733 | { |
734 | float64 ret = float64_muladd(f2, f3, f1, float_muladd_negate_c, |
735 | &env->fpu_status); |
736 | handle_exceptions(env, false, GETPC()); |
737 | return ret; |
738 | } |
739 | |
740 | /* The rightmost bit has the number 11. */ |
741 | static inline uint16_t dcmask(int bit, bool neg) |
742 | { |
743 | return 1 << (11 - bit - neg); |
744 | } |
745 | |
746 | #define DEF_FLOAT_DCMASK(_TYPE) \ |
747 | uint16_t _TYPE##_dcmask(CPUS390XState *env, _TYPE f1) \ |
748 | { \ |
749 | const bool neg = _TYPE##_is_neg(f1); \ |
750 | \ |
751 | /* Sorted by most common cases - only one class is possible */ \ |
752 | if (_TYPE##_is_normal(f1)) { \ |
753 | return dcmask(2, neg); \ |
754 | } else if (_TYPE##_is_zero(f1)) { \ |
755 | return dcmask(0, neg); \ |
756 | } else if (_TYPE##_is_denormal(f1)) { \ |
757 | return dcmask(4, neg); \ |
758 | } else if (_TYPE##_is_infinity(f1)) { \ |
759 | return dcmask(6, neg); \ |
760 | } else if (_TYPE##_is_quiet_nan(f1, &env->fpu_status)) { \ |
761 | return dcmask(8, neg); \ |
762 | } \ |
763 | /* signaling nan, as last remaining case */ \ |
764 | return dcmask(10, neg); \ |
765 | } |
766 | DEF_FLOAT_DCMASK(float32) |
767 | DEF_FLOAT_DCMASK(float64) |
768 | DEF_FLOAT_DCMASK(float128) |
769 | |
770 | /* test data class 32-bit */ |
771 | uint32_t HELPER(tceb)(CPUS390XState *env, uint64_t f1, uint64_t m2) |
772 | { |
773 | return (m2 & float32_dcmask(env, f1)) != 0; |
774 | } |
775 | |
776 | /* test data class 64-bit */ |
777 | uint32_t HELPER(tcdb)(CPUS390XState *env, uint64_t v1, uint64_t m2) |
778 | { |
779 | return (m2 & float64_dcmask(env, v1)) != 0; |
780 | } |
781 | |
782 | /* test data class 128-bit */ |
783 | uint32_t HELPER(tcxb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t m2) |
784 | { |
785 | return (m2 & float128_dcmask(env, make_float128(ah, al))) != 0; |
786 | } |
787 | |
788 | /* square root 32-bit */ |
789 | uint64_t HELPER(sqeb)(CPUS390XState *env, uint64_t f2) |
790 | { |
791 | float32 ret = float32_sqrt(f2, &env->fpu_status); |
792 | handle_exceptions(env, false, GETPC()); |
793 | return ret; |
794 | } |
795 | |
796 | /* square root 64-bit */ |
797 | uint64_t HELPER(sqdb)(CPUS390XState *env, uint64_t f2) |
798 | { |
799 | float64 ret = float64_sqrt(f2, &env->fpu_status); |
800 | handle_exceptions(env, false, GETPC()); |
801 | return ret; |
802 | } |
803 | |
804 | /* square root 128-bit */ |
805 | uint64_t HELPER(sqxb)(CPUS390XState *env, uint64_t ah, uint64_t al) |
806 | { |
807 | float128 ret = float128_sqrt(make_float128(ah, al), &env->fpu_status); |
808 | handle_exceptions(env, false, GETPC()); |
809 | return RET128(ret); |
810 | } |
811 | |
812 | static const int fpc_to_rnd[8] = { |
813 | float_round_nearest_even, |
814 | float_round_to_zero, |
815 | float_round_up, |
816 | float_round_down, |
817 | -1, |
818 | -1, |
819 | -1, |
820 | float_round_to_odd, |
821 | }; |
822 | |
823 | /* set fpc */ |
824 | void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc) |
825 | { |
826 | if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u || |
827 | (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) { |
828 | s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); |
829 | } |
830 | |
831 | /* Install everything in the main FPC. */ |
832 | env->fpc = fpc; |
833 | |
834 | /* Install the rounding mode in the shadow fpu_status. */ |
835 | set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status); |
836 | } |
837 | |
838 | /* set fpc and signal */ |
839 | void HELPER(sfas)(CPUS390XState *env, uint64_t fpc) |
840 | { |
841 | uint32_t signalling = env->fpc; |
842 | uint32_t s390_exc; |
843 | |
844 | if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u || |
845 | (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) { |
846 | s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); |
847 | } |
848 | |
849 | /* |
850 | * FPC is set to the FPC operand with a bitwise OR of the signalling |
851 | * flags. |
852 | */ |
853 | env->fpc = fpc | (signalling & 0x00ff0000); |
854 | set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status); |
855 | |
856 | /* |
857 | * If any signaling flag is enabled in the new FPC mask, a |
858 | * simulated-iee-exception exception occurs. |
859 | */ |
860 | s390_exc = (signalling >> 16) & (fpc >> 24); |
861 | if (s390_exc) { |
862 | if (s390_exc & S390_IEEE_MASK_INVALID) { |
863 | s390_exc = S390_IEEE_MASK_INVALID; |
864 | } else if (s390_exc & S390_IEEE_MASK_DIVBYZERO) { |
865 | s390_exc = S390_IEEE_MASK_DIVBYZERO; |
866 | } else if (s390_exc & S390_IEEE_MASK_OVERFLOW) { |
867 | s390_exc &= (S390_IEEE_MASK_OVERFLOW | S390_IEEE_MASK_INEXACT); |
868 | } else if (s390_exc & S390_IEEE_MASK_UNDERFLOW) { |
869 | s390_exc &= (S390_IEEE_MASK_UNDERFLOW | S390_IEEE_MASK_INEXACT); |
870 | } else if (s390_exc & S390_IEEE_MASK_INEXACT) { |
871 | s390_exc = S390_IEEE_MASK_INEXACT; |
872 | } else if (s390_exc & S390_IEEE_MASK_QUANTUM) { |
873 | s390_exc = S390_IEEE_MASK_QUANTUM; |
874 | } |
875 | tcg_s390_data_exception(env, s390_exc | 3, GETPC()); |
876 | } |
877 | } |
878 | |
879 | /* set bfp rounding mode */ |
880 | void HELPER(srnm)(CPUS390XState *env, uint64_t rnd) |
881 | { |
882 | if (rnd > 0x7 || fpc_to_rnd[rnd & 0x7] == -1) { |
883 | s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); |
884 | } |
885 | |
886 | env->fpc = deposit32(env->fpc, 0, 3, rnd); |
887 | set_float_rounding_mode(fpc_to_rnd[rnd & 0x7], &env->fpu_status); |
888 | } |
889 | |