1 /*
2 * Helpers for HPPA FPU instructions.
3 *
4 * Copyright (c) 2016 Richard Henderson <rth@twiddle.net>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "qemu/osdep.h"
21 #include "cpu.h"
22 #include "exec/exec-all.h"
23 #include "exec/helper-proto.h"
24 #include "fpu/softfloat.h"
25
HELPER(loaded_fr0)26 void HELPER(loaded_fr0)(CPUHPPAState *env)
27 {
28 uint32_t shadow = env->fr[0] >> 32;
29 int rm, d;
30
31 env->fr0_shadow = shadow;
32
33 switch (FIELD_EX32(shadow, FPSR, RM)) {
34 default:
35 rm = float_round_nearest_even;
36 break;
37 case 1:
38 rm = float_round_to_zero;
39 break;
40 case 2:
41 rm = float_round_up;
42 break;
43 case 3:
44 rm = float_round_down;
45 break;
46 }
47 set_float_rounding_mode(rm, &env->fp_status);
48
49 d = FIELD_EX32(shadow, FPSR, D);
50 set_flush_to_zero(d, &env->fp_status);
51 set_flush_inputs_to_zero(d, &env->fp_status);
52
53 /*
54 * TODO: we only need to do this at CPU reset, but currently
55 * HPPA does note implement a CPU reset method at all...
56 */
57 set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fp_status);
58 /*
59 * TODO: The HPPA architecture reference only documents its NaN
60 * propagation rule for 2-operand operations. Testing on real hardware
61 * might be necessary to confirm whether this order for muladd is correct.
62 * Not preferring the SNaN is almost certainly incorrect as it diverges
63 * from the documented rules for 2-operand operations.
64 */
65 set_float_3nan_prop_rule(float_3nan_prop_abc, &env->fp_status);
66 /* For inf * 0 + NaN, return the input NaN */
67 set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status);
68 /* Default NaN: sign bit clear, msb-1 frac bit set */
69 set_float_default_nan_pattern(0b00100000, &env->fp_status);
70 set_snan_bit_is_one(true, &env->fp_status);
71 /*
72 * "PA-RISC 2.0 Architecture" says it is IMPDEF whether the flushing
73 * enabled by FPSR.D happens before or after rounding. We pick "before"
74 * for consistency with tininess detection.
75 */
76 set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status);
77 /*
78 * TODO: "PA-RISC 2.0 Architecture" chapter 10 says that we should
79 * detect tininess before rounding, but we don't set that here so we
80 * get the default tininess after rounding.
81 */
82 }
83
cpu_hppa_loaded_fr0(CPUHPPAState * env)84 void cpu_hppa_loaded_fr0(CPUHPPAState *env)
85 {
86 helper_loaded_fr0(env);
87 }
88
89 #define CONVERT_BIT(X, SRC, DST) \
90 ((unsigned)(SRC) > (unsigned)(DST) \
91 ? (X) / ((SRC) / (DST)) & (DST) \
92 : ((X) & (SRC)) * ((DST) / (SRC)))
93
update_fr0_op(CPUHPPAState * env,uintptr_t ra)94 static void update_fr0_op(CPUHPPAState *env, uintptr_t ra)
95 {
96 uint32_t soft_exp = get_float_exception_flags(&env->fp_status);
97 uint32_t hard_exp = 0;
98 uint32_t shadow = env->fr0_shadow & 0x3ffffff;
99 uint32_t fr1 = 0;
100
101 if (likely(soft_exp == 0)) {
102 env->fr[0] = (uint64_t)shadow << 32;
103 return;
104 }
105 set_float_exception_flags(0, &env->fp_status);
106
107 hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, R_FPSR_ENA_I_MASK);
108 hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, R_FPSR_ENA_U_MASK);
109 hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, R_FPSR_ENA_O_MASK);
110 hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK);
111 hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, R_FPSR_ENA_V_MASK);
112 if (hard_exp & shadow) {
113 shadow = FIELD_DP32(shadow, FPSR, T, 1);
114 /* fill exception register #1, which is lower 32-bits of fr[0] */
115 #if !defined(CONFIG_USER_ONLY)
116 if (hard_exp & (R_FPSR_ENA_O_MASK | R_FPSR_ENA_U_MASK)) {
117 /* over- and underflow both set overflow flag only */
118 fr1 = FIELD_DP32(fr1, FPSR, C, 1);
119 fr1 = FIELD_DP32(fr1, FPSR, FLG_O, 1);
120 } else
121 #endif
122 {
123 fr1 |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT);
124 }
125 }
126 env->fr0_shadow = shadow;
127 env->fr[0] = (uint64_t)shadow << 32 | fr1;
128
129 if (hard_exp & shadow) {
130 hppa_dynamic_excp(env, EXCP_ASSIST, ra);
131 }
132 }
133
HELPER(fsqrt_s)134 float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg)
135 {
136 float32 ret = float32_sqrt(arg, &env->fp_status);
137 update_fr0_op(env, GETPC());
138 return ret;
139 }
140
HELPER(frnd_s)141 float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg)
142 {
143 float32 ret = float32_round_to_int(arg, &env->fp_status);
144 update_fr0_op(env, GETPC());
145 return ret;
146 }
147
HELPER(fadd_s)148 float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b)
149 {
150 float32 ret = float32_add(a, b, &env->fp_status);
151 update_fr0_op(env, GETPC());
152 return ret;
153 }
154
HELPER(fsub_s)155 float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b)
156 {
157 float32 ret = float32_sub(a, b, &env->fp_status);
158 update_fr0_op(env, GETPC());
159 return ret;
160 }
161
HELPER(fmpy_s)162 float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b)
163 {
164 float32 ret = float32_mul(a, b, &env->fp_status);
165 update_fr0_op(env, GETPC());
166 return ret;
167 }
168
HELPER(fdiv_s)169 float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b)
170 {
171 float32 ret = float32_div(a, b, &env->fp_status);
172 update_fr0_op(env, GETPC());
173 return ret;
174 }
175
HELPER(fsqrt_d)176 float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg)
177 {
178 float64 ret = float64_sqrt(arg, &env->fp_status);
179 update_fr0_op(env, GETPC());
180 return ret;
181 }
182
HELPER(frnd_d)183 float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg)
184 {
185 float64 ret = float64_round_to_int(arg, &env->fp_status);
186 update_fr0_op(env, GETPC());
187 return ret;
188 }
189
HELPER(fadd_d)190 float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b)
191 {
192 float64 ret = float64_add(a, b, &env->fp_status);
193 update_fr0_op(env, GETPC());
194 return ret;
195 }
196
HELPER(fsub_d)197 float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b)
198 {
199 float64 ret = float64_sub(a, b, &env->fp_status);
200 update_fr0_op(env, GETPC());
201 return ret;
202 }
203
HELPER(fmpy_d)204 float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b)
205 {
206 float64 ret = float64_mul(a, b, &env->fp_status);
207 update_fr0_op(env, GETPC());
208 return ret;
209 }
210
HELPER(fdiv_d)211 float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b)
212 {
213 float64 ret = float64_div(a, b, &env->fp_status);
214 update_fr0_op(env, GETPC());
215 return ret;
216 }
217
HELPER(fcnv_s_d)218 float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg)
219 {
220 float64 ret = float32_to_float64(arg, &env->fp_status);
221 update_fr0_op(env, GETPC());
222 return ret;
223 }
224
HELPER(fcnv_d_s)225 float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg)
226 {
227 float32 ret = float64_to_float32(arg, &env->fp_status);
228 update_fr0_op(env, GETPC());
229 return ret;
230 }
231
HELPER(fcnv_w_s)232 float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg)
233 {
234 float32 ret = int32_to_float32(arg, &env->fp_status);
235 update_fr0_op(env, GETPC());
236 return ret;
237 }
238
HELPER(fcnv_dw_s)239 float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg)
240 {
241 float32 ret = int64_to_float32(arg, &env->fp_status);
242 update_fr0_op(env, GETPC());
243 return ret;
244 }
245
HELPER(fcnv_w_d)246 float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg)
247 {
248 float64 ret = int32_to_float64(arg, &env->fp_status);
249 update_fr0_op(env, GETPC());
250 return ret;
251 }
252
HELPER(fcnv_dw_d)253 float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg)
254 {
255 float64 ret = int64_to_float64(arg, &env->fp_status);
256 update_fr0_op(env, GETPC());
257 return ret;
258 }
259
HELPER(fcnv_s_w)260 int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg)
261 {
262 int32_t ret = float32_to_int32(arg, &env->fp_status);
263 update_fr0_op(env, GETPC());
264 return ret;
265 }
266
HELPER(fcnv_d_w)267 int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg)
268 {
269 int32_t ret = float64_to_int32(arg, &env->fp_status);
270 update_fr0_op(env, GETPC());
271 return ret;
272 }
273
HELPER(fcnv_s_dw)274 int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg)
275 {
276 int64_t ret = float32_to_int64(arg, &env->fp_status);
277 update_fr0_op(env, GETPC());
278 return ret;
279 }
280
HELPER(fcnv_d_dw)281 int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg)
282 {
283 int64_t ret = float64_to_int64(arg, &env->fp_status);
284 update_fr0_op(env, GETPC());
285 return ret;
286 }
287
HELPER(fcnv_t_s_w)288 int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg)
289 {
290 int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status);
291 update_fr0_op(env, GETPC());
292 return ret;
293 }
294
HELPER(fcnv_t_d_w)295 int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg)
296 {
297 int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status);
298 update_fr0_op(env, GETPC());
299 return ret;
300 }
301
HELPER(fcnv_t_s_dw)302 int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg)
303 {
304 int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status);
305 update_fr0_op(env, GETPC());
306 return ret;
307 }
308
HELPER(fcnv_t_d_dw)309 int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg)
310 {
311 int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status);
312 update_fr0_op(env, GETPC());
313 return ret;
314 }
315
HELPER(fcnv_uw_s)316 float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg)
317 {
318 float32 ret = uint32_to_float32(arg, &env->fp_status);
319 update_fr0_op(env, GETPC());
320 return ret;
321 }
322
HELPER(fcnv_udw_s)323 float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg)
324 {
325 float32 ret = uint64_to_float32(arg, &env->fp_status);
326 update_fr0_op(env, GETPC());
327 return ret;
328 }
329
HELPER(fcnv_uw_d)330 float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg)
331 {
332 float64 ret = uint32_to_float64(arg, &env->fp_status);
333 update_fr0_op(env, GETPC());
334 return ret;
335 }
336
HELPER(fcnv_udw_d)337 float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg)
338 {
339 float64 ret = uint64_to_float64(arg, &env->fp_status);
340 update_fr0_op(env, GETPC());
341 return ret;
342 }
343
HELPER(fcnv_s_uw)344 uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg)
345 {
346 uint32_t ret = float32_to_uint32(arg, &env->fp_status);
347 update_fr0_op(env, GETPC());
348 return ret;
349 }
350
HELPER(fcnv_d_uw)351 uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg)
352 {
353 uint32_t ret = float64_to_uint32(arg, &env->fp_status);
354 update_fr0_op(env, GETPC());
355 return ret;
356 }
357
HELPER(fcnv_s_udw)358 uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg)
359 {
360 uint64_t ret = float32_to_uint64(arg, &env->fp_status);
361 update_fr0_op(env, GETPC());
362 return ret;
363 }
364
HELPER(fcnv_d_udw)365 uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg)
366 {
367 uint64_t ret = float64_to_uint64(arg, &env->fp_status);
368 update_fr0_op(env, GETPC());
369 return ret;
370 }
371
HELPER(fcnv_t_s_uw)372 uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg)
373 {
374 uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status);
375 update_fr0_op(env, GETPC());
376 return ret;
377 }
378
HELPER(fcnv_t_d_uw)379 uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg)
380 {
381 uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status);
382 update_fr0_op(env, GETPC());
383 return ret;
384 }
385
HELPER(fcnv_t_s_udw)386 uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg)
387 {
388 uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status);
389 update_fr0_op(env, GETPC());
390 return ret;
391 }
392
HELPER(fcnv_t_d_udw)393 uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg)
394 {
395 uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status);
396 update_fr0_op(env, GETPC());
397 return ret;
398 }
399
update_fr0_cmp(CPUHPPAState * env,uint32_t y,uint32_t c,FloatRelation r)400 static void update_fr0_cmp(CPUHPPAState *env, uint32_t y,
401 uint32_t c, FloatRelation r)
402 {
403 uint32_t shadow = env->fr0_shadow;
404
405 switch (r) {
406 case float_relation_greater:
407 c = extract32(c, 4, 1);
408 break;
409 case float_relation_less:
410 c = extract32(c, 3, 1);
411 break;
412 case float_relation_equal:
413 c = extract32(c, 2, 1);
414 break;
415 case float_relation_unordered:
416 c = extract32(c, 1, 1);
417 break;
418 default:
419 g_assert_not_reached();
420 }
421
422 if (y) {
423 /* targeted comparison */
424 /* set fpsr[ca[y - 1]] to current compare */
425 shadow = deposit32(shadow, R_FPSR_CA0_SHIFT - (y - 1), 1, c);
426 } else {
427 /* queued comparison */
428 /* shift cq right by one place */
429 shadow = (shadow & ~R_FPSR_CQ_MASK) | ((shadow >> 1) & R_FPSR_CQ_MASK);
430 /* move fpsr[c] to fpsr[cq[0]] */
431 shadow = FIELD_DP32(shadow, FPSR, CQ0, FIELD_EX32(shadow, FPSR, C));
432 /* set fpsr[c] to current compare */
433 shadow = FIELD_DP32(shadow, FPSR, C, c);
434 }
435
436 env->fr0_shadow = shadow;
437 env->fr[0] = (uint64_t)shadow << 32;
438 }
439
HELPER(fcmp_s)440 void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b,
441 uint32_t y, uint32_t c)
442 {
443 FloatRelation r;
444 if (c & 1) {
445 r = float32_compare(a, b, &env->fp_status);
446 } else {
447 r = float32_compare_quiet(a, b, &env->fp_status);
448 }
449 update_fr0_op(env, GETPC());
450 update_fr0_cmp(env, y, c, r);
451 }
452
HELPER(fcmp_d)453 void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b,
454 uint32_t y, uint32_t c)
455 {
456 FloatRelation r;
457 if (c & 1) {
458 r = float64_compare(a, b, &env->fp_status);
459 } else {
460 r = float64_compare_quiet(a, b, &env->fp_status);
461 }
462 update_fr0_op(env, GETPC());
463 update_fr0_cmp(env, y, c, r);
464 }
465
HELPER(fmpyfadd_s)466 float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c)
467 {
468 float32 ret = float32_muladd(a, b, c, 0, &env->fp_status);
469 update_fr0_op(env, GETPC());
470 return ret;
471 }
472
HELPER(fmpynfadd_s)473 float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c)
474 {
475 float32 ret = float32_muladd(a, b, c, float_muladd_negate_product,
476 &env->fp_status);
477 update_fr0_op(env, GETPC());
478 return ret;
479 }
480
HELPER(fmpyfadd_d)481 float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c)
482 {
483 float64 ret = float64_muladd(a, b, c, 0, &env->fp_status);
484 update_fr0_op(env, GETPC());
485 return ret;
486 }
487
HELPER(fmpynfadd_d)488 float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c)
489 {
490 float64 ret = float64_muladd(a, b, c, float_muladd_negate_product,
491 &env->fp_status);
492 update_fr0_op(env, GETPC());
493 return ret;
494 }
495