xref: /qemu/target/tricore/fpu_helper.c (revision 815061b9da88a3a9a90fae58b5a778632e0cc2df)
1 /*
2  *  TriCore emulation for qemu: fpu helper.
3  *
4  *  Copyright (c) 2016 Bastian Koppelmann University of Paderborn
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/helper-proto.h"
23 #include "fpu/softfloat.h"
24 
25 #define QUIET_NAN 0x7fc00000
26 #define ADD_NAN   0x7fc00001
27 #define SQRT_NAN  0x7fc00004
28 #define DIV_NAN   0x7fc00008
29 #define MUL_NAN   0x7fc00002
30 #define FPU_FS PSW_USB_C
31 #define FPU_FI PSW_USB_V
32 #define FPU_FV PSW_USB_SV
33 #define FPU_FZ PSW_USB_AV
34 #define FPU_FU PSW_USB_SAV
35 
36 #define float32_sqrt_nan make_float32(SQRT_NAN)
37 #define float32_quiet_nan make_float32(QUIET_NAN)
38 
39 /* we don't care about input_denormal */
40 static inline uint8_t f_get_excp_flags(CPUTriCoreState *env)
41 {
42     return get_float_exception_flags(&env->fp_status)
43            & (float_flag_invalid
44               | float_flag_overflow
45               | float_flag_underflow
46               | float_flag_output_denormal
47               | float_flag_divbyzero
48               | float_flag_inexact);
49 }
50 
51 static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2,
52                                            float32 arg3, float32 result,
53                                            uint32_t muladd_negate_c)
54 {
55     uint32_t aSign, bSign, cSign;
56     uint32_t aExp, bExp, cExp;
57 
58     if (float32_is_any_nan(arg1) || float32_is_any_nan(arg2) ||
59         float32_is_any_nan(arg3)) {
60         return QUIET_NAN;
61     } else if (float32_is_infinity(arg1) && float32_is_zero(arg2)) {
62         return MUL_NAN;
63     } else if (float32_is_zero(arg1) && float32_is_infinity(arg2)) {
64         return MUL_NAN;
65     } else {
66         aSign = arg1 >> 31;
67         bSign = arg2 >> 31;
68         cSign = arg3 >> 31;
69 
70         aExp = (arg1 >> 23) & 0xff;
71         bExp = (arg2 >> 23) & 0xff;
72         cExp = (arg3 >> 23) & 0xff;
73 
74         if (muladd_negate_c) {
75             cSign ^= 1;
76         }
77         if (((aExp == 0xff) || (bExp == 0xff)) && (cExp == 0xff)) {
78             if (aSign ^ bSign ^ cSign) {
79                 return ADD_NAN;
80             }
81         }
82     }
83 
84     return result;
85 }
86 
87 static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags)
88 {
89     uint8_t some_excp = 0;
90     set_float_exception_flags(0, &env->fp_status);
91 
92     if (flags & float_flag_invalid) {
93         env->FPU_FI = 1 << 31;
94         some_excp = 1;
95     }
96 
97     if (flags & float_flag_overflow) {
98         env->FPU_FV = 1 << 31;
99         some_excp = 1;
100     }
101 
102     if (flags & float_flag_underflow || flags & float_flag_output_denormal) {
103         env->FPU_FU = 1 << 31;
104         some_excp = 1;
105     }
106 
107     if (flags & float_flag_divbyzero) {
108         env->FPU_FZ = 1 << 31;
109         some_excp = 1;
110     }
111 
112     if (flags & float_flag_inexact || flags & float_flag_output_denormal) {
113         env->PSW |= 1 << 26;
114         some_excp = 1;
115     }
116 
117     env->FPU_FS = some_excp;
118 }
119 
120 #define FADD_SUB(op)                                                           \
121 uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2)          \
122 {                                                                              \
123     float32 arg1 = make_float32(r1);                                           \
124     float32 arg2 = make_float32(r2);                                           \
125     uint32_t flags;                                                            \
126     float32 f_result;                                                          \
127                                                                                \
128     f_result = float32_##op(arg2, arg1, &env->fp_status);                      \
129     flags = f_get_excp_flags(env);                                             \
130     if (flags) {                                                               \
131         /* If the output is a NaN, but the inputs aren't,                      \
132            we return a unique value.  */                                       \
133         if ((flags & float_flag_invalid)                                       \
134             && !float32_is_any_nan(arg1)                                       \
135             && !float32_is_any_nan(arg2)) {                                    \
136             f_result = ADD_NAN;                                                \
137         }                                                                      \
138         f_update_psw_flags(env, flags);                                        \
139     } else {                                                                   \
140         env->FPU_FS = 0;                                                       \
141     }                                                                          \
142     return (uint32_t)f_result;                                                 \
143 }
144 FADD_SUB(add)
145 FADD_SUB(sub)
146 
147 uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
148 {
149     uint32_t flags;
150     float32 arg1 = make_float32(r1);
151     float32 arg2 = make_float32(r2);
152     float32 f_result;
153 
154     f_result = float32_mul(arg1, arg2, &env->fp_status);
155 
156     flags = f_get_excp_flags(env);
157     if (flags) {
158         /* If the output is a NaN, but the inputs aren't,
159            we return a unique value.  */
160         if ((flags & float_flag_invalid)
161             && !float32_is_any_nan(arg1)
162             && !float32_is_any_nan(arg2)) {
163                 f_result = MUL_NAN;
164         }
165         f_update_psw_flags(env, flags);
166     } else {
167         env->FPU_FS = 0;
168     }
169     return (uint32_t)f_result;
170 
171 }
172 
173 /*
174  * Target TriCore QSEED.F significand Lookup Table
175  *
176  * The QSEED.F output significand depends on the least-significant
177  * exponent bit and the 6 most-significant significand bits.
178  *
179  * IEEE 754 float datatype
180  * partitioned into Sign (S), Exponent (E) and Significand (M):
181  *
182  * S   E E E E E E E E   M M M M M M ...
183  *    |             |               |
184  *    +------+------+-------+-------+
185  *           |              |
186  *          for        lookup table
187  *      calculating     index for
188  *        output E       output M
189  *
190  * This lookup table was extracted by analyzing QSEED output
191  * from the real hardware
192  */
193 static const uint8_t target_qseed_significand_table[128] = {
194     253, 252, 245, 244, 239, 238, 231, 230, 225, 224, 217, 216,
195     211, 210, 205, 204, 201, 200, 195, 194, 189, 188, 185, 184,
196     179, 178, 175, 174, 169, 168, 165, 164, 161, 160, 157, 156,
197     153, 152, 149, 148, 145, 144, 141, 140, 137, 136, 133, 132,
198     131, 130, 127, 126, 123, 122, 121, 120, 117, 116, 115, 114,
199     111, 110, 109, 108, 103, 102, 99, 98, 93, 92, 89, 88, 83,
200     82, 79, 78, 75, 74, 71, 70, 67, 66, 63, 62, 59, 58, 55,
201     54, 53, 52, 49, 48, 45, 44, 43, 42, 39, 38, 37, 36, 33,
202     32, 31, 30, 27, 26, 25, 24, 23, 22, 19, 18, 17, 16, 15,
203     14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2
204 };
205 
206 uint32_t helper_qseed(CPUTriCoreState *env, uint32_t r1)
207 {
208     uint32_t arg1, S, E, M, E_minus_one, m_idx;
209     uint32_t new_E, new_M, new_S, result;
210 
211     arg1 = make_float32(r1);
212 
213     /* fetch IEEE-754 fields S, E and the uppermost 6-bit of M */
214     S = extract32(arg1, 31, 1);
215     E = extract32(arg1, 23, 8);
216     M = extract32(arg1, 17, 6);
217 
218     if (float32_is_any_nan(arg1)) {
219         result = float32_quiet_nan;
220     } else if (float32_is_zero_or_denormal(arg1)) {
221         if (float32_is_neg(arg1)) {
222             result = float32_infinity | (1 << 31);
223         } else {
224             result = float32_infinity;
225         }
226     } else if (float32_is_neg(arg1)) {
227         result = float32_sqrt_nan;
228     } else if (float32_is_infinity(arg1)) {
229         result = float32_zero;
230     } else {
231         E_minus_one = E - 1;
232         m_idx = ((E_minus_one & 1) << 6) | M;
233         new_S = S;
234         new_E = 0xBD - E_minus_one / 2;
235         new_M = target_qseed_significand_table[m_idx];
236 
237         result = 0;
238         result = deposit32(result, 31, 1, new_S);
239         result = deposit32(result, 23, 8, new_E);
240         result = deposit32(result, 15, 8, new_M);
241     }
242 
243     if (float32_is_signaling_nan(arg1, &env->fp_status)
244         || result == float32_sqrt_nan) {
245         env->FPU_FI = 1 << 31;
246         env->FPU_FS = 1;
247     } else {
248         env->FPU_FS = 0;
249     }
250 
251     return (uint32_t) result;
252 }
253 
254 uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
255 {
256     uint32_t flags;
257     float32 arg1 = make_float32(r1);
258     float32 arg2 = make_float32(r2);
259     float32 f_result;
260 
261     f_result = float32_div(arg1, arg2 , &env->fp_status);
262 
263     flags = f_get_excp_flags(env);
264     if (flags) {
265         /* If the output is a NaN, but the inputs aren't,
266            we return a unique value.  */
267         if ((flags & float_flag_invalid)
268             && !float32_is_any_nan(arg1)
269             && !float32_is_any_nan(arg2)) {
270                 f_result = DIV_NAN;
271         }
272         f_update_psw_flags(env, flags);
273     } else {
274         env->FPU_FS = 0;
275     }
276 
277     return (uint32_t)f_result;
278 }
279 
280 uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1,
281                       uint32_t r2, uint32_t r3)
282 {
283     uint32_t flags;
284     float32 arg1 = make_float32(r1);
285     float32 arg2 = make_float32(r2);
286     float32 arg3 = make_float32(r3);
287     float32 f_result;
288 
289     f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status);
290 
291     flags = f_get_excp_flags(env);
292     if (flags) {
293         if (flags & float_flag_invalid) {
294             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
295             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
296             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
297             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0);
298         }
299         f_update_psw_flags(env, flags);
300     } else {
301         env->FPU_FS = 0;
302     }
303     return (uint32_t)f_result;
304 }
305 
306 uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1,
307                       uint32_t r2, uint32_t r3)
308 {
309     uint32_t flags;
310     float32 arg1 = make_float32(r1);
311     float32 arg2 = make_float32(r2);
312     float32 arg3 = make_float32(r3);
313     float32 f_result;
314 
315     f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product,
316                               &env->fp_status);
317 
318     flags = f_get_excp_flags(env);
319     if (flags) {
320         if (flags & float_flag_invalid) {
321             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
322             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
323             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
324 
325             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1);
326         }
327         f_update_psw_flags(env, flags);
328     } else {
329         env->FPU_FS = 0;
330     }
331     return (uint32_t)f_result;
332 }
333 
334 uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
335 {
336     uint32_t result, flags;
337     float32 arg1 = make_float32(r1);
338     float32 arg2 = make_float32(r2);
339 
340     set_flush_inputs_to_zero(0, &env->fp_status);
341 
342     result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1);
343     result |= float32_is_denormal(arg1) << 4;
344     result |= float32_is_denormal(arg2) << 5;
345 
346     flags = f_get_excp_flags(env);
347     if (flags) {
348         f_update_psw_flags(env, flags);
349     } else {
350         env->FPU_FS = 0;
351     }
352 
353     set_flush_inputs_to_zero(1, &env->fp_status);
354     return result;
355 }
356 
357 uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg)
358 {
359     float32 f_arg = make_float32(arg);
360     int32_t result, flags;
361 
362     result = float32_to_int32(f_arg, &env->fp_status);
363 
364     flags = f_get_excp_flags(env);
365     if (flags) {
366         if (float32_is_any_nan(f_arg)) {
367             result = 0;
368         }
369         f_update_psw_flags(env, flags);
370     } else {
371         env->FPU_FS = 0;
372     }
373     return (uint32_t)result;
374 }
375 
376 uint32_t helper_ftohp(CPUTriCoreState *env, uint32_t arg)
377 {
378     float32 f_arg = make_float32(arg);
379     uint32_t result = 0;
380     int32_t flags = 0;
381 
382     /*
383      * if we have any NAN we need to move the top 2 and lower 8 input mantissa
384      * bits to the top 2 and lower 8 output mantissa bits respectively.
385      * Softfloat on the other hand uses the top 10 mantissa bits.
386      */
387     if (float32_is_any_nan(f_arg)) {
388         if (float32_is_signaling_nan(f_arg, &env->fp_status)) {
389             flags |= float_flag_invalid;
390         }
391         result = float16_set_sign(result, arg >> 31);
392         result = deposit32(result, 10, 5, 0x1f);
393         result = deposit32(result, 8, 2, extract32(arg, 21, 2));
394         result = deposit32(result, 0, 8, extract32(arg, 0, 8));
395         if (extract32(result, 0, 10) == 0) {
396             result |= (1 << 8);
397         }
398     } else {
399         set_flush_to_zero(0, &env->fp_status);
400         result = float32_to_float16(f_arg, true, &env->fp_status);
401         set_flush_to_zero(1, &env->fp_status);
402         flags = f_get_excp_flags(env);
403     }
404 
405     if (flags) {
406         f_update_psw_flags(env, flags);
407     } else {
408         env->FPU_FS = 0;
409     }
410 
411     return result;
412 }
413 
414 uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg)
415 {
416     float32 f_result;
417     uint32_t flags;
418     f_result = int32_to_float32(arg, &env->fp_status);
419 
420     flags = f_get_excp_flags(env);
421     if (flags) {
422         f_update_psw_flags(env, flags);
423     } else {
424         env->FPU_FS = 0;
425     }
426     return (uint32_t)f_result;
427 }
428 
429 uint32_t helper_utof(CPUTriCoreState *env, uint32_t arg)
430 {
431     float32 f_result;
432     uint32_t flags;
433 
434     f_result = uint32_to_float32(arg, &env->fp_status);
435 
436     flags = f_get_excp_flags(env);
437     if (flags) {
438         f_update_psw_flags(env, flags);
439     } else {
440         env->FPU_FS = 0;
441     }
442     return (uint32_t)f_result;
443 }
444 
445 uint32_t helper_ftoiz(CPUTriCoreState *env, uint32_t arg)
446 {
447     float32 f_arg = make_float32(arg);
448     uint32_t result;
449     int32_t flags;
450 
451     result = float32_to_int32_round_to_zero(f_arg, &env->fp_status);
452 
453     flags = f_get_excp_flags(env);
454     if (flags & float_flag_invalid) {
455         flags &= ~float_flag_inexact;
456         if (float32_is_any_nan(f_arg)) {
457             result = 0;
458         }
459     }
460 
461     if (flags) {
462         f_update_psw_flags(env, flags);
463     } else {
464         env->FPU_FS = 0;
465     }
466 
467     return result;
468 }
469 
470 uint32_t helper_ftou(CPUTriCoreState *env, uint32_t arg)
471 {
472     float32 f_arg = make_float32(arg);
473     uint32_t result;
474     int32_t flags = 0;
475 
476     result = float32_to_uint32(f_arg, &env->fp_status);
477 
478     flags = f_get_excp_flags(env);
479     if (flags & float_flag_invalid) {
480         flags &= ~float_flag_inexact;
481         if (float32_is_any_nan(f_arg)) {
482             result = 0;
483         }
484     /*
485      * we need to check arg < 0.0 before rounding as TriCore needs to raise
486      * float_flag_invalid as well. For instance, when we have a negative
487      * exponent and sign, softfloat would only raise float_flat_inexact.
488      */
489     } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) {
490         flags = float_flag_invalid;
491         result = 0;
492     }
493 
494     if (flags) {
495         f_update_psw_flags(env, flags);
496     } else {
497         env->FPU_FS = 0;
498     }
499     return result;
500 }
501 
502 uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg)
503 {
504     float32 f_arg = make_float32(arg);
505     uint32_t result;
506     int32_t flags;
507 
508     result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status);
509 
510     flags = f_get_excp_flags(env);
511     if (flags & float_flag_invalid) {
512         flags &= ~float_flag_inexact;
513         if (float32_is_any_nan(f_arg)) {
514             result = 0;
515         }
516     /*
517      * we need to check arg < 0.0 before rounding as TriCore needs to raise
518      * float_flag_invalid as well. For instance, when we have a negative
519      * exponent and sign, softfloat would only raise float_flat_inexact.
520      */
521     } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) {
522         flags = float_flag_invalid;
523         result = 0;
524     }
525 
526     if (flags) {
527         f_update_psw_flags(env, flags);
528     } else {
529         env->FPU_FS = 0;
530     }
531     return result;
532 }
533 
534 void helper_updfl(CPUTriCoreState *env, uint32_t arg)
535 {
536     env->FPU_FS =  extract32(arg, 7, 1) & extract32(arg, 15, 1);
537     env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31;
538     env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31;
539     env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31;
540     env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31;
541     /* clear FX and RM */
542     env->PSW &= ~(extract32(arg, 10, 1) << 26);
543     env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26;
544 
545     fpu_set_state(env);
546 }
547