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 */
f_get_excp_flags(CPUTriCoreState * env)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 
f_maddsub_nan_result(float32 arg1,float32 arg2,float32 arg3,float32 result,uint32_t muladd_negate_c)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 
f_update_psw_flags(CPUTriCoreState * env,uint8_t flags)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)
FADD_SUB(sub)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 
helper_qseed(CPUTriCoreState * env,uint32_t r1)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 
helper_fdiv(CPUTriCoreState * env,uint32_t r1,uint32_t r2)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 
helper_fmadd(CPUTriCoreState * env,uint32_t r1,uint32_t r2,uint32_t r3)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 
helper_fmsub(CPUTriCoreState * env,uint32_t r1,uint32_t r2,uint32_t r3)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 
helper_fcmp(CPUTriCoreState * env,uint32_t r1,uint32_t r2)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 
helper_ftoi(CPUTriCoreState * env,uint32_t arg)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 
helper_itof(CPUTriCoreState * env,uint32_t arg)376 uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg)
377 {
378     float32 f_result;
379     uint32_t flags;
380     f_result = int32_to_float32(arg, &env->fp_status);
381 
382     flags = f_get_excp_flags(env);
383     if (flags) {
384         f_update_psw_flags(env, flags);
385     } else {
386         env->FPU_FS = 0;
387     }
388     return (uint32_t)f_result;
389 }
390 
helper_utof(CPUTriCoreState * env,uint32_t arg)391 uint32_t helper_utof(CPUTriCoreState *env, uint32_t arg)
392 {
393     float32 f_result;
394     uint32_t flags;
395 
396     f_result = uint32_to_float32(arg, &env->fp_status);
397 
398     flags = f_get_excp_flags(env);
399     if (flags) {
400         f_update_psw_flags(env, flags);
401     } else {
402         env->FPU_FS = 0;
403     }
404     return (uint32_t)f_result;
405 }
406 
helper_ftoiz(CPUTriCoreState * env,uint32_t arg)407 uint32_t helper_ftoiz(CPUTriCoreState *env, uint32_t arg)
408 {
409     float32 f_arg = make_float32(arg);
410     uint32_t result;
411     int32_t flags;
412 
413     result = float32_to_int32_round_to_zero(f_arg, &env->fp_status);
414 
415     flags = f_get_excp_flags(env);
416     if (flags & float_flag_invalid) {
417         flags &= ~float_flag_inexact;
418         if (float32_is_any_nan(f_arg)) {
419             result = 0;
420         }
421     }
422 
423     if (flags) {
424         f_update_psw_flags(env, flags);
425     } else {
426         env->FPU_FS = 0;
427     }
428 
429     return result;
430 }
431 
helper_ftouz(CPUTriCoreState * env,uint32_t arg)432 uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg)
433 {
434     float32 f_arg = make_float32(arg);
435     uint32_t result;
436     int32_t flags;
437 
438     result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status);
439 
440     flags = f_get_excp_flags(env);
441     if (flags & float_flag_invalid) {
442         flags &= ~float_flag_inexact;
443         if (float32_is_any_nan(f_arg)) {
444             result = 0;
445         }
446     } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) {
447         flags = float_flag_invalid;
448         result = 0;
449     }
450 
451     if (flags) {
452         f_update_psw_flags(env, flags);
453     } else {
454         env->FPU_FS = 0;
455     }
456     return result;
457 }
458 
helper_updfl(CPUTriCoreState * env,uint32_t arg)459 void helper_updfl(CPUTriCoreState *env, uint32_t arg)
460 {
461     env->FPU_FS =  extract32(arg, 7, 1) & extract32(arg, 15, 1);
462     env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31;
463     env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31;
464     env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31;
465     env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31;
466     /* clear FX and RM */
467     env->PSW &= ~(extract32(arg, 10, 1) << 26);
468     env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26;
469 
470     fpu_set_state(env);
471 }
472