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