xref: /openbmc/qemu/target/hppa/fpu_helper.c (revision 6966e0ba1d4a9e6fe76f618c2c9c73900107fdc8)
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