xref: /qemu/target/hppa/fpu_helper.c (revision 8a2b516ba2855c4530388051de2b8d17bc780ea8)
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 
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 
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 
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;
99 
100     if (likely(soft_exp == 0)) {
101         env->fr[0] = (uint64_t)shadow << 32;
102         return;
103     }
104     set_float_exception_flags(0, &env->fp_status);
105 
106     hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact,   R_FPSR_ENA_I_MASK);
107     hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, R_FPSR_ENA_U_MASK);
108     hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow,  R_FPSR_ENA_O_MASK);
109     hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK);
110     hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid,   R_FPSR_ENA_V_MASK);
111     shadow |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT);
112     env->fr0_shadow = shadow;
113     env->fr[0] = (uint64_t)shadow << 32;
114 
115     if (hard_exp & shadow) {
116         hppa_dynamic_excp(env, EXCP_ASSIST, ra);
117     }
118 }
119 
120 float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg)
121 {
122     float32 ret = float32_sqrt(arg, &env->fp_status);
123     update_fr0_op(env, GETPC());
124     return ret;
125 }
126 
127 float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg)
128 {
129     float32 ret = float32_round_to_int(arg, &env->fp_status);
130     update_fr0_op(env, GETPC());
131     return ret;
132 }
133 
134 float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b)
135 {
136     float32 ret = float32_add(a, b, &env->fp_status);
137     update_fr0_op(env, GETPC());
138     return ret;
139 }
140 
141 float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b)
142 {
143     float32 ret = float32_sub(a, b, &env->fp_status);
144     update_fr0_op(env, GETPC());
145     return ret;
146 }
147 
148 float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b)
149 {
150     float32 ret = float32_mul(a, b, &env->fp_status);
151     update_fr0_op(env, GETPC());
152     return ret;
153 }
154 
155 float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b)
156 {
157     float32 ret = float32_div(a, b, &env->fp_status);
158     update_fr0_op(env, GETPC());
159     return ret;
160 }
161 
162 float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg)
163 {
164     float64 ret = float64_sqrt(arg, &env->fp_status);
165     update_fr0_op(env, GETPC());
166     return ret;
167 }
168 
169 float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg)
170 {
171     float64 ret = float64_round_to_int(arg, &env->fp_status);
172     update_fr0_op(env, GETPC());
173     return ret;
174 }
175 
176 float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b)
177 {
178     float64 ret = float64_add(a, b, &env->fp_status);
179     update_fr0_op(env, GETPC());
180     return ret;
181 }
182 
183 float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b)
184 {
185     float64 ret = float64_sub(a, b, &env->fp_status);
186     update_fr0_op(env, GETPC());
187     return ret;
188 }
189 
190 float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b)
191 {
192     float64 ret = float64_mul(a, b, &env->fp_status);
193     update_fr0_op(env, GETPC());
194     return ret;
195 }
196 
197 float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b)
198 {
199     float64 ret = float64_div(a, b, &env->fp_status);
200     update_fr0_op(env, GETPC());
201     return ret;
202 }
203 
204 float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg)
205 {
206     float64 ret = float32_to_float64(arg, &env->fp_status);
207     update_fr0_op(env, GETPC());
208     return ret;
209 }
210 
211 float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg)
212 {
213     float32 ret = float64_to_float32(arg, &env->fp_status);
214     update_fr0_op(env, GETPC());
215     return ret;
216 }
217 
218 float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg)
219 {
220     float32 ret = int32_to_float32(arg, &env->fp_status);
221     update_fr0_op(env, GETPC());
222     return ret;
223 }
224 
225 float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg)
226 {
227     float32 ret = int64_to_float32(arg, &env->fp_status);
228     update_fr0_op(env, GETPC());
229     return ret;
230 }
231 
232 float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg)
233 {
234     float64 ret = int32_to_float64(arg, &env->fp_status);
235     update_fr0_op(env, GETPC());
236     return ret;
237 }
238 
239 float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg)
240 {
241     float64 ret = int64_to_float64(arg, &env->fp_status);
242     update_fr0_op(env, GETPC());
243     return ret;
244 }
245 
246 int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg)
247 {
248     int32_t ret = float32_to_int32(arg, &env->fp_status);
249     update_fr0_op(env, GETPC());
250     return ret;
251 }
252 
253 int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg)
254 {
255     int32_t ret = float64_to_int32(arg, &env->fp_status);
256     update_fr0_op(env, GETPC());
257     return ret;
258 }
259 
260 int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg)
261 {
262     int64_t ret = float32_to_int64(arg, &env->fp_status);
263     update_fr0_op(env, GETPC());
264     return ret;
265 }
266 
267 int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg)
268 {
269     int64_t ret = float64_to_int64(arg, &env->fp_status);
270     update_fr0_op(env, GETPC());
271     return ret;
272 }
273 
274 int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg)
275 {
276     int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status);
277     update_fr0_op(env, GETPC());
278     return ret;
279 }
280 
281 int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg)
282 {
283     int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status);
284     update_fr0_op(env, GETPC());
285     return ret;
286 }
287 
288 int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg)
289 {
290     int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status);
291     update_fr0_op(env, GETPC());
292     return ret;
293 }
294 
295 int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg)
296 {
297     int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status);
298     update_fr0_op(env, GETPC());
299     return ret;
300 }
301 
302 float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg)
303 {
304     float32 ret = uint32_to_float32(arg, &env->fp_status);
305     update_fr0_op(env, GETPC());
306     return ret;
307 }
308 
309 float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg)
310 {
311     float32 ret = uint64_to_float32(arg, &env->fp_status);
312     update_fr0_op(env, GETPC());
313     return ret;
314 }
315 
316 float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg)
317 {
318     float64 ret = uint32_to_float64(arg, &env->fp_status);
319     update_fr0_op(env, GETPC());
320     return ret;
321 }
322 
323 float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg)
324 {
325     float64 ret = uint64_to_float64(arg, &env->fp_status);
326     update_fr0_op(env, GETPC());
327     return ret;
328 }
329 
330 uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg)
331 {
332     uint32_t ret = float32_to_uint32(arg, &env->fp_status);
333     update_fr0_op(env, GETPC());
334     return ret;
335 }
336 
337 uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg)
338 {
339     uint32_t ret = float64_to_uint32(arg, &env->fp_status);
340     update_fr0_op(env, GETPC());
341     return ret;
342 }
343 
344 uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg)
345 {
346     uint64_t ret = float32_to_uint64(arg, &env->fp_status);
347     update_fr0_op(env, GETPC());
348     return ret;
349 }
350 
351 uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg)
352 {
353     uint64_t ret = float64_to_uint64(arg, &env->fp_status);
354     update_fr0_op(env, GETPC());
355     return ret;
356 }
357 
358 uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg)
359 {
360     uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status);
361     update_fr0_op(env, GETPC());
362     return ret;
363 }
364 
365 uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg)
366 {
367     uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status);
368     update_fr0_op(env, GETPC());
369     return ret;
370 }
371 
372 uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg)
373 {
374     uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status);
375     update_fr0_op(env, GETPC());
376     return ret;
377 }
378 
379 uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg)
380 {
381     uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status);
382     update_fr0_op(env, GETPC());
383     return ret;
384 }
385 
386 static void update_fr0_cmp(CPUHPPAState *env, uint32_t y,
387                            uint32_t c, FloatRelation r)
388 {
389     uint32_t shadow = env->fr0_shadow;
390 
391     switch (r) {
392     case float_relation_greater:
393         c = extract32(c, 4, 1);
394         break;
395     case float_relation_less:
396         c = extract32(c, 3, 1);
397         break;
398     case float_relation_equal:
399         c = extract32(c, 2, 1);
400         break;
401     case float_relation_unordered:
402         c = extract32(c, 1, 1);
403         break;
404     default:
405         g_assert_not_reached();
406     }
407 
408     if (y) {
409         /* targeted comparison */
410         /* set fpsr[ca[y - 1]] to current compare */
411         shadow = deposit32(shadow, R_FPSR_CA0_SHIFT - (y - 1), 1, c);
412     } else {
413         /* queued comparison */
414         /* shift cq right by one place */
415         shadow = (shadow & ~R_FPSR_CQ_MASK) | ((shadow >> 1) & R_FPSR_CQ_MASK);
416         /* move fpsr[c] to fpsr[cq[0]] */
417         shadow = FIELD_DP32(shadow, FPSR, CQ0, FIELD_EX32(shadow, FPSR, C));
418         /* set fpsr[c] to current compare */
419         shadow = FIELD_DP32(shadow, FPSR, C, c);
420     }
421 
422     env->fr0_shadow = shadow;
423     env->fr[0] = (uint64_t)shadow << 32;
424 }
425 
426 void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b,
427                     uint32_t y, uint32_t c)
428 {
429     FloatRelation r;
430     if (c & 1) {
431         r = float32_compare(a, b, &env->fp_status);
432     } else {
433         r = float32_compare_quiet(a, b, &env->fp_status);
434     }
435     update_fr0_op(env, GETPC());
436     update_fr0_cmp(env, y, c, r);
437 }
438 
439 void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b,
440                     uint32_t y, uint32_t c)
441 {
442     FloatRelation r;
443     if (c & 1) {
444         r = float64_compare(a, b, &env->fp_status);
445     } else {
446         r = float64_compare_quiet(a, b, &env->fp_status);
447     }
448     update_fr0_op(env, GETPC());
449     update_fr0_cmp(env, y, c, r);
450 }
451 
452 float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c)
453 {
454     float32 ret = float32_muladd(a, b, c, 0, &env->fp_status);
455     update_fr0_op(env, GETPC());
456     return ret;
457 }
458 
459 float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c)
460 {
461     float32 ret = float32_muladd(a, b, c, float_muladd_negate_product,
462                                  &env->fp_status);
463     update_fr0_op(env, GETPC());
464     return ret;
465 }
466 
467 float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c)
468 {
469     float64 ret = float64_muladd(a, b, c, 0, &env->fp_status);
470     update_fr0_op(env, GETPC());
471     return ret;
472 }
473 
474 float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c)
475 {
476     float64 ret = float64_muladd(a, b, c, float_muladd_negate_product,
477                                  &env->fp_status);
478     update_fr0_op(env, GETPC());
479     return ret;
480 }
481