xref: /openbmc/qemu/target/sparc/fop_helper.c (revision c5614ee3)
1 /*
2  * FPU op helpers
3  *
4  *  Copyright (c) 2003-2005 Fabrice Bellard
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 static inline float128 f128_in(Int128 i)
27 {
28     union {
29         Int128 i;
30         float128 f;
31     } u;
32 
33     u.i = i;
34     return u.f;
35 }
36 
37 static inline Int128 f128_ret(float128 f)
38 {
39     union {
40         Int128 i;
41         float128 f;
42     } u;
43 
44     u.f = f;
45     return u.i;
46 }
47 
48 static void check_ieee_exceptions(CPUSPARCState *env, uintptr_t ra)
49 {
50     target_ulong status = get_float_exception_flags(&env->fp_status);
51     uint32_t cexc = 0;
52 
53     if (unlikely(status)) {
54         /* Keep exception flags clear for next time.  */
55         set_float_exception_flags(0, &env->fp_status);
56 
57         /* Copy IEEE 754 flags into FSR */
58         if (status & float_flag_invalid) {
59             cexc |= FSR_NVC;
60         }
61         if (status & float_flag_overflow) {
62             cexc |= FSR_OFC;
63         }
64         if (status & float_flag_underflow) {
65             cexc |= FSR_UFC;
66         }
67         if (status & float_flag_divbyzero) {
68             cexc |= FSR_DZC;
69         }
70         if (status & float_flag_inexact) {
71             cexc |= FSR_NXC;
72         }
73 
74         if (cexc & (env->fsr >> FSR_TEM_SHIFT)) {
75             /* Unmasked exception, generate an IEEE trap. */
76             env->fsr_cexc_ftt = cexc | FSR_FTT_IEEE_EXCP;
77             cpu_raise_exception_ra(env, TT_FP_EXCP, ra);
78         }
79 
80         /* Accumulate exceptions */
81         env->fsr |= cexc << FSR_AEXC_SHIFT;
82     }
83 
84     /* No trap, so FTT is cleared. */
85     env->fsr_cexc_ftt = cexc;
86 }
87 
88 float32 helper_fadds(CPUSPARCState *env, float32 src1, float32 src2)
89 {
90     float32 ret = float32_add(src1, src2, &env->fp_status);
91     check_ieee_exceptions(env, GETPC());
92     return ret;
93 }
94 
95 float32 helper_fsubs(CPUSPARCState *env, float32 src1, float32 src2)
96 {
97     float32 ret = float32_sub(src1, src2, &env->fp_status);
98     check_ieee_exceptions(env, GETPC());
99     return ret;
100 }
101 
102 float32 helper_fmuls(CPUSPARCState *env, float32 src1, float32 src2)
103 {
104     float32 ret = float32_mul(src1, src2, &env->fp_status);
105     check_ieee_exceptions(env, GETPC());
106     return ret;
107 }
108 
109 float32 helper_fdivs(CPUSPARCState *env, float32 src1, float32 src2)
110 {
111     float32 ret = float32_div(src1, src2, &env->fp_status);
112     check_ieee_exceptions(env, GETPC());
113     return ret;
114 }
115 
116 float64 helper_faddd(CPUSPARCState *env, float64 src1, float64 src2)
117 {
118     float64 ret = float64_add(src1, src2, &env->fp_status);
119     check_ieee_exceptions(env, GETPC());
120     return ret;
121 }
122 
123 float64 helper_fsubd(CPUSPARCState *env, float64 src1, float64 src2)
124 {
125     float64 ret = float64_sub(src1, src2, &env->fp_status);
126     check_ieee_exceptions(env, GETPC());
127     return ret;
128 }
129 
130 float64 helper_fmuld(CPUSPARCState *env, float64 src1, float64 src2)
131 {
132     float64 ret = float64_mul(src1, src2, &env->fp_status);
133     check_ieee_exceptions(env, GETPC());
134     return ret;
135 }
136 
137 float64 helper_fdivd(CPUSPARCState *env, float64 src1, float64 src2)
138 {
139     float64 ret = float64_div(src1, src2, &env->fp_status);
140     check_ieee_exceptions(env, GETPC());
141     return ret;
142 }
143 
144 Int128 helper_faddq(CPUSPARCState *env, Int128 src1, Int128 src2)
145 {
146     float128 ret = float128_add(f128_in(src1), f128_in(src2), &env->fp_status);
147     check_ieee_exceptions(env, GETPC());
148     return f128_ret(ret);
149 }
150 
151 Int128 helper_fsubq(CPUSPARCState *env, Int128 src1, Int128 src2)
152 {
153     float128 ret = float128_sub(f128_in(src1), f128_in(src2), &env->fp_status);
154     check_ieee_exceptions(env, GETPC());
155     return f128_ret(ret);
156 }
157 
158 Int128 helper_fmulq(CPUSPARCState *env, Int128 src1, Int128 src2)
159 {
160     float128 ret = float128_mul(f128_in(src1), f128_in(src2), &env->fp_status);
161     check_ieee_exceptions(env, GETPC());
162     return f128_ret(ret);
163 }
164 
165 Int128 helper_fdivq(CPUSPARCState *env, Int128 src1, Int128 src2)
166 {
167     float128 ret = float128_div(f128_in(src1), f128_in(src2), &env->fp_status);
168     check_ieee_exceptions(env, GETPC());
169     return f128_ret(ret);
170 }
171 
172 float64 helper_fsmuld(CPUSPARCState *env, float32 src1, float32 src2)
173 {
174     float64 ret = float64_mul(float32_to_float64(src1, &env->fp_status),
175                               float32_to_float64(src2, &env->fp_status),
176                               &env->fp_status);
177     check_ieee_exceptions(env, GETPC());
178     return ret;
179 }
180 
181 Int128 helper_fdmulq(CPUSPARCState *env, float64 src1, float64 src2)
182 {
183     float128 ret = float128_mul(float64_to_float128(src1, &env->fp_status),
184                                 float64_to_float128(src2, &env->fp_status),
185                                 &env->fp_status);
186     check_ieee_exceptions(env, GETPC());
187     return f128_ret(ret);
188 }
189 
190 /* Integer to float conversion.  */
191 float32 helper_fitos(CPUSPARCState *env, int32_t src)
192 {
193     float32 ret = int32_to_float32(src, &env->fp_status);
194     check_ieee_exceptions(env, GETPC());
195     return ret;
196 }
197 
198 float64 helper_fitod(CPUSPARCState *env, int32_t src)
199 {
200     float64 ret = int32_to_float64(src, &env->fp_status);
201     check_ieee_exceptions(env, GETPC());
202     return ret;
203 }
204 
205 Int128 helper_fitoq(CPUSPARCState *env, int32_t src)
206 {
207     float128 ret = int32_to_float128(src, &env->fp_status);
208     check_ieee_exceptions(env, GETPC());
209     return f128_ret(ret);
210 }
211 
212 #ifdef TARGET_SPARC64
213 float32 helper_fxtos(CPUSPARCState *env, int64_t src)
214 {
215     float32 ret = int64_to_float32(src, &env->fp_status);
216     check_ieee_exceptions(env, GETPC());
217     return ret;
218 }
219 
220 float64 helper_fxtod(CPUSPARCState *env, int64_t src)
221 {
222     float64 ret = int64_to_float64(src, &env->fp_status);
223     check_ieee_exceptions(env, GETPC());
224     return ret;
225 }
226 
227 Int128 helper_fxtoq(CPUSPARCState *env, int64_t src)
228 {
229     float128 ret = int64_to_float128(src, &env->fp_status);
230     check_ieee_exceptions(env, GETPC());
231     return f128_ret(ret);
232 }
233 #endif
234 
235 /* floating point conversion */
236 float32 helper_fdtos(CPUSPARCState *env, float64 src)
237 {
238     float32 ret = float64_to_float32(src, &env->fp_status);
239     check_ieee_exceptions(env, GETPC());
240     return ret;
241 }
242 
243 float64 helper_fstod(CPUSPARCState *env, float32 src)
244 {
245     float64 ret = float32_to_float64(src, &env->fp_status);
246     check_ieee_exceptions(env, GETPC());
247     return ret;
248 }
249 
250 float32 helper_fqtos(CPUSPARCState *env, Int128 src)
251 {
252     float32 ret = float128_to_float32(f128_in(src), &env->fp_status);
253     check_ieee_exceptions(env, GETPC());
254     return ret;
255 }
256 
257 Int128 helper_fstoq(CPUSPARCState *env, float32 src)
258 {
259     float128 ret = float32_to_float128(src, &env->fp_status);
260     check_ieee_exceptions(env, GETPC());
261     return f128_ret(ret);
262 }
263 
264 float64 helper_fqtod(CPUSPARCState *env, Int128 src)
265 {
266     float64 ret = float128_to_float64(f128_in(src), &env->fp_status);
267     check_ieee_exceptions(env, GETPC());
268     return ret;
269 }
270 
271 Int128 helper_fdtoq(CPUSPARCState *env, float64 src)
272 {
273     float128 ret = float64_to_float128(src, &env->fp_status);
274     check_ieee_exceptions(env, GETPC());
275     return f128_ret(ret);
276 }
277 
278 /* Float to integer conversion.  */
279 int32_t helper_fstoi(CPUSPARCState *env, float32 src)
280 {
281     int32_t ret = float32_to_int32_round_to_zero(src, &env->fp_status);
282     check_ieee_exceptions(env, GETPC());
283     return ret;
284 }
285 
286 int32_t helper_fdtoi(CPUSPARCState *env, float64 src)
287 {
288     int32_t ret = float64_to_int32_round_to_zero(src, &env->fp_status);
289     check_ieee_exceptions(env, GETPC());
290     return ret;
291 }
292 
293 int32_t helper_fqtoi(CPUSPARCState *env, Int128 src)
294 {
295     int32_t ret = float128_to_int32_round_to_zero(f128_in(src),
296                                                   &env->fp_status);
297     check_ieee_exceptions(env, GETPC());
298     return ret;
299 }
300 
301 #ifdef TARGET_SPARC64
302 int64_t helper_fstox(CPUSPARCState *env, float32 src)
303 {
304     int64_t ret = float32_to_int64_round_to_zero(src, &env->fp_status);
305     check_ieee_exceptions(env, GETPC());
306     return ret;
307 }
308 
309 int64_t helper_fdtox(CPUSPARCState *env, float64 src)
310 {
311     int64_t ret = float64_to_int64_round_to_zero(src, &env->fp_status);
312     check_ieee_exceptions(env, GETPC());
313     return ret;
314 }
315 
316 int64_t helper_fqtox(CPUSPARCState *env, Int128 src)
317 {
318     int64_t ret = float128_to_int64_round_to_zero(f128_in(src),
319                                                   &env->fp_status);
320     check_ieee_exceptions(env, GETPC());
321     return ret;
322 }
323 #endif
324 
325 float32 helper_fsqrts(CPUSPARCState *env, float32 src)
326 {
327     float32 ret = float32_sqrt(src, &env->fp_status);
328     check_ieee_exceptions(env, GETPC());
329     return ret;
330 }
331 
332 float64 helper_fsqrtd(CPUSPARCState *env, float64 src)
333 {
334     float64 ret = float64_sqrt(src, &env->fp_status);
335     check_ieee_exceptions(env, GETPC());
336     return ret;
337 }
338 
339 Int128 helper_fsqrtq(CPUSPARCState *env, Int128 src)
340 {
341     float128 ret = float128_sqrt(f128_in(src), &env->fp_status);
342     check_ieee_exceptions(env, GETPC());
343     return f128_ret(ret);
344 }
345 
346 float32 helper_fmadds(CPUSPARCState *env, float32 s1,
347                       float32 s2, float32 s3, uint32_t op)
348 {
349     float32 ret = float32_muladd(s1, s2, s3, op, &env->fp_status);
350     check_ieee_exceptions(env, GETPC());
351     return ret;
352 }
353 
354 float64 helper_fmaddd(CPUSPARCState *env, float64 s1,
355                       float64 s2, float64 s3, uint32_t op)
356 {
357     float64 ret = float64_muladd(s1, s2, s3, op, &env->fp_status);
358     check_ieee_exceptions(env, GETPC());
359     return ret;
360 }
361 
362 float32 helper_fnadds(CPUSPARCState *env, float32 src1, float32 src2)
363 {
364     float32 ret = float32_add(src1, src2, &env->fp_status);
365 
366     /*
367      * NaN inputs or result do not get a sign change.
368      * Nor, apparently, does zero: on hardware, -(x + -x) yields +0.
369      */
370     if (!float32_is_any_nan(ret) && !float32_is_zero(ret)) {
371         ret = float32_chs(ret);
372     }
373     check_ieee_exceptions(env, GETPC());
374     return ret;
375 }
376 
377 float32 helper_fnmuls(CPUSPARCState *env, float32 src1, float32 src2)
378 {
379     float32 ret = float32_mul(src1, src2, &env->fp_status);
380 
381     /* NaN inputs or result do not get a sign change. */
382     if (!float32_is_any_nan(ret)) {
383         ret = float32_chs(ret);
384     }
385     check_ieee_exceptions(env, GETPC());
386     return ret;
387 }
388 
389 float64 helper_fnaddd(CPUSPARCState *env, float64 src1, float64 src2)
390 {
391     float64 ret = float64_add(src1, src2, &env->fp_status);
392 
393     /*
394      * NaN inputs or result do not get a sign change.
395      * Nor, apparently, does zero: on hardware, -(x + -x) yields +0.
396      */
397     if (!float64_is_any_nan(ret) && !float64_is_zero(ret)) {
398         ret = float64_chs(ret);
399     }
400     check_ieee_exceptions(env, GETPC());
401     return ret;
402 }
403 
404 float64 helper_fnmuld(CPUSPARCState *env, float64 src1, float64 src2)
405 {
406     float64 ret = float64_mul(src1, src2, &env->fp_status);
407 
408     /* NaN inputs or result do not get a sign change. */
409     if (!float64_is_any_nan(ret)) {
410         ret = float64_chs(ret);
411     }
412     check_ieee_exceptions(env, GETPC());
413     return ret;
414 }
415 
416 float64 helper_fnsmuld(CPUSPARCState *env, float32 src1, float32 src2)
417 {
418     float64 ret = float64_mul(float32_to_float64(src1, &env->fp_status),
419                               float32_to_float64(src2, &env->fp_status),
420                               &env->fp_status);
421 
422     /* NaN inputs or result do not get a sign change. */
423     if (!float64_is_any_nan(ret)) {
424         ret = float64_chs(ret);
425     }
426     check_ieee_exceptions(env, GETPC());
427     return ret;
428 }
429 
430 static uint32_t finish_fcmp(CPUSPARCState *env, FloatRelation r, uintptr_t ra)
431 {
432     check_ieee_exceptions(env, ra);
433 
434     /*
435      * FCC values:
436      * 0 =
437      * 1 <
438      * 2 >
439      * 3 unordered
440      */
441     switch (r) {
442     case float_relation_equal:
443         return 0;
444     case float_relation_less:
445         return 1;
446     case float_relation_greater:
447         return 2;
448     case float_relation_unordered:
449         env->fsr |= FSR_NVA;
450         return 3;
451     }
452     g_assert_not_reached();
453 }
454 
455 uint32_t helper_fcmps(CPUSPARCState *env, float32 src1, float32 src2)
456 {
457     FloatRelation r = float32_compare_quiet(src1, src2, &env->fp_status);
458     return finish_fcmp(env, r, GETPC());
459 }
460 
461 uint32_t helper_fcmpes(CPUSPARCState *env, float32 src1, float32 src2)
462 {
463     FloatRelation r = float32_compare(src1, src2, &env->fp_status);
464     return finish_fcmp(env, r, GETPC());
465 }
466 
467 uint32_t helper_fcmpd(CPUSPARCState *env, float64 src1, float64 src2)
468 {
469     FloatRelation r = float64_compare_quiet(src1, src2, &env->fp_status);
470     return finish_fcmp(env, r, GETPC());
471 }
472 
473 uint32_t helper_fcmped(CPUSPARCState *env, float64 src1, float64 src2)
474 {
475     FloatRelation r = float64_compare(src1, src2, &env->fp_status);
476     return finish_fcmp(env, r, GETPC());
477 }
478 
479 uint32_t helper_fcmpq(CPUSPARCState *env, Int128 src1, Int128 src2)
480 {
481     FloatRelation r = float128_compare_quiet(f128_in(src1), f128_in(src2),
482                                              &env->fp_status);
483     return finish_fcmp(env, r, GETPC());
484 }
485 
486 uint32_t helper_fcmpeq(CPUSPARCState *env, Int128 src1, Int128 src2)
487 {
488     FloatRelation r = float128_compare(f128_in(src1), f128_in(src2),
489                                        &env->fp_status);
490     return finish_fcmp(env, r, GETPC());
491 }
492 
493 uint32_t helper_flcmps(float32 src1, float32 src2)
494 {
495     /*
496      * FLCMP never raises an exception nor modifies any FSR fields.
497      * Perform the comparison with a dummy fp environment.
498      */
499     float_status discard = { };
500     FloatRelation r = float32_compare_quiet(src1, src2, &discard);
501 
502     switch (r) {
503     case float_relation_equal:
504         if (src2 == float32_zero && src1 != float32_zero) {
505             return 1;  /* -0.0 < +0.0 */
506         }
507         return 0;
508     case float_relation_less:
509         return 1;
510     case float_relation_greater:
511         return 0;
512     case float_relation_unordered:
513         return float32_is_any_nan(src2) ? 3 : 2;
514     }
515     g_assert_not_reached();
516 }
517 
518 uint32_t helper_flcmpd(float64 src1, float64 src2)
519 {
520     float_status discard = { };
521     FloatRelation r = float64_compare_quiet(src1, src2, &discard);
522 
523     switch (r) {
524     case float_relation_equal:
525         if (src2 == float64_zero && src1 != float64_zero) {
526             return 1;  /* -0.0 < +0.0 */
527         }
528         return 0;
529     case float_relation_less:
530         return 1;
531     case float_relation_greater:
532         return 0;
533     case float_relation_unordered:
534         return float64_is_any_nan(src2) ? 3 : 2;
535     }
536     g_assert_not_reached();
537 }
538 
539 target_ulong cpu_get_fsr(CPUSPARCState *env)
540 {
541     target_ulong fsr = env->fsr | env->fsr_cexc_ftt;
542 
543     fsr |= env->fcc[0] << FSR_FCC0_SHIFT;
544 #ifdef TARGET_SPARC64
545     fsr |= (uint64_t)env->fcc[1] << FSR_FCC1_SHIFT;
546     fsr |= (uint64_t)env->fcc[2] << FSR_FCC2_SHIFT;
547     fsr |= (uint64_t)env->fcc[3] << FSR_FCC3_SHIFT;
548 #endif
549 
550     /* VER is kept completely separate until re-assembly. */
551     fsr |= env->def.fpu_version;
552 
553     return fsr;
554 }
555 
556 target_ulong helper_get_fsr(CPUSPARCState *env)
557 {
558     return cpu_get_fsr(env);
559 }
560 
561 static void set_fsr_nonsplit(CPUSPARCState *env, target_ulong fsr)
562 {
563     int rnd_mode;
564 
565     env->fsr = fsr & (FSR_RD_MASK | FSR_TEM_MASK | FSR_AEXC_MASK);
566 
567     switch (fsr & FSR_RD_MASK) {
568     case FSR_RD_NEAREST:
569         rnd_mode = float_round_nearest_even;
570         break;
571     default:
572     case FSR_RD_ZERO:
573         rnd_mode = float_round_to_zero;
574         break;
575     case FSR_RD_POS:
576         rnd_mode = float_round_up;
577         break;
578     case FSR_RD_NEG:
579         rnd_mode = float_round_down;
580         break;
581     }
582     set_float_rounding_mode(rnd_mode, &env->fp_status);
583 }
584 
585 void cpu_put_fsr(CPUSPARCState *env, target_ulong fsr)
586 {
587     env->fsr_cexc_ftt = fsr & (FSR_CEXC_MASK | FSR_FTT_MASK);
588 
589     env->fcc[0] = extract32(fsr, FSR_FCC0_SHIFT, 2);
590 #ifdef TARGET_SPARC64
591     env->fcc[1] = extract64(fsr, FSR_FCC1_SHIFT, 2);
592     env->fcc[2] = extract64(fsr, FSR_FCC2_SHIFT, 2);
593     env->fcc[3] = extract64(fsr, FSR_FCC3_SHIFT, 2);
594 #endif
595 
596     set_fsr_nonsplit(env, fsr);
597 }
598 
599 void helper_set_fsr_nofcc_noftt(CPUSPARCState *env, uint32_t fsr)
600 {
601     env->fsr_cexc_ftt &= FSR_FTT_MASK;
602     env->fsr_cexc_ftt |= fsr & FSR_CEXC_MASK;
603     set_fsr_nonsplit(env, fsr);
604 }
605 
606 void helper_set_fsr_nofcc(CPUSPARCState *env, uint32_t fsr)
607 {
608     env->fsr_cexc_ftt = fsr & (FSR_CEXC_MASK | FSR_FTT_MASK);
609     set_fsr_nonsplit(env, fsr);
610 }
611