xref: /qemu/target/tricore/fpu_helper.c (revision ddd7fead8c8524dc51b9b7c20b1f08a4b34ef653)
1996a729fSBastian Koppelmann /*
2996a729fSBastian Koppelmann  *  TriCore emulation for qemu: fpu helper.
3996a729fSBastian Koppelmann  *
4996a729fSBastian Koppelmann  *  Copyright (c) 2016 Bastian Koppelmann University of Paderborn
5996a729fSBastian Koppelmann  *
6996a729fSBastian Koppelmann  * This library is free software; you can redistribute it and/or
7996a729fSBastian Koppelmann  * modify it under the terms of the GNU Lesser General Public
8996a729fSBastian Koppelmann  * License as published by the Free Software Foundation; either
9996a729fSBastian Koppelmann  * version 2 of the License, or (at your option) any later version.
10996a729fSBastian Koppelmann  *
11996a729fSBastian Koppelmann  * This library is distributed in the hope that it will be useful,
12996a729fSBastian Koppelmann  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13996a729fSBastian Koppelmann  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14996a729fSBastian Koppelmann  * Lesser General Public License for more details.
15996a729fSBastian Koppelmann  *
16996a729fSBastian Koppelmann  * You should have received a copy of the GNU Lesser General Public
17996a729fSBastian Koppelmann  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18996a729fSBastian Koppelmann  */
19996a729fSBastian Koppelmann 
20996a729fSBastian Koppelmann #include "qemu/osdep.h"
21996a729fSBastian Koppelmann #include "cpu.h"
22996a729fSBastian Koppelmann #include "exec/helper-proto.h"
23996a729fSBastian Koppelmann 
24*ddd7feadSBastian Koppelmann #define QUIET_NAN 0x7fc00000
25*ddd7feadSBastian Koppelmann #define ADD_NAN   0x7fc00001
26996a729fSBastian Koppelmann #define DIV_NAN   0x7fc00008
27996a729fSBastian Koppelmann #define MUL_NAN   0x7fc00002
28996a729fSBastian Koppelmann #define FPU_FS PSW_USB_C
29996a729fSBastian Koppelmann #define FPU_FI PSW_USB_V
30996a729fSBastian Koppelmann #define FPU_FV PSW_USB_SV
31996a729fSBastian Koppelmann #define FPU_FZ PSW_USB_AV
32996a729fSBastian Koppelmann #define FPU_FU PSW_USB_SAV
33996a729fSBastian Koppelmann 
34996a729fSBastian Koppelmann /* we don't care about input_denormal */
35996a729fSBastian Koppelmann static inline uint8_t f_get_excp_flags(CPUTriCoreState *env)
36996a729fSBastian Koppelmann {
37996a729fSBastian Koppelmann     return get_float_exception_flags(&env->fp_status)
38996a729fSBastian Koppelmann            & (float_flag_invalid
39996a729fSBastian Koppelmann               | float_flag_overflow
40996a729fSBastian Koppelmann               | float_flag_underflow
41996a729fSBastian Koppelmann               | float_flag_output_denormal
42996a729fSBastian Koppelmann               | float_flag_divbyzero
43996a729fSBastian Koppelmann               | float_flag_inexact);
44996a729fSBastian Koppelmann }
45996a729fSBastian Koppelmann 
46996a729fSBastian Koppelmann static inline bool f_is_denormal(float32 arg)
47996a729fSBastian Koppelmann {
48996a729fSBastian Koppelmann     return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg);
49996a729fSBastian Koppelmann }
50996a729fSBastian Koppelmann 
51*ddd7feadSBastian Koppelmann static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2,
52*ddd7feadSBastian Koppelmann                                            float32 arg3, float32 result,
53*ddd7feadSBastian Koppelmann                                            uint32_t muladd_negate_c)
54*ddd7feadSBastian Koppelmann {
55*ddd7feadSBastian Koppelmann     uint32_t aSign, bSign, cSign;
56*ddd7feadSBastian Koppelmann     uint32_t aExp, bExp, cExp;
57*ddd7feadSBastian Koppelmann 
58*ddd7feadSBastian Koppelmann     if (float32_is_any_nan(arg1) || float32_is_any_nan(arg2) ||
59*ddd7feadSBastian Koppelmann         float32_is_any_nan(arg3)) {
60*ddd7feadSBastian Koppelmann         return QUIET_NAN;
61*ddd7feadSBastian Koppelmann     } else if (float32_is_infinity(arg1) && float32_is_zero(arg2)) {
62*ddd7feadSBastian Koppelmann         return MUL_NAN;
63*ddd7feadSBastian Koppelmann     } else if (float32_is_zero(arg1) && float32_is_infinity(arg2)) {
64*ddd7feadSBastian Koppelmann         return MUL_NAN;
65*ddd7feadSBastian Koppelmann     } else {
66*ddd7feadSBastian Koppelmann         aSign = arg1 >> 31;
67*ddd7feadSBastian Koppelmann         bSign = arg2 >> 31;
68*ddd7feadSBastian Koppelmann         cSign = arg3 >> 31;
69*ddd7feadSBastian Koppelmann 
70*ddd7feadSBastian Koppelmann         aExp = (arg1 >> 23) & 0xff;
71*ddd7feadSBastian Koppelmann         bExp = (arg2 >> 23) & 0xff;
72*ddd7feadSBastian Koppelmann         cExp = (arg3 >> 23) & 0xff;
73*ddd7feadSBastian Koppelmann 
74*ddd7feadSBastian Koppelmann         if (muladd_negate_c) {
75*ddd7feadSBastian Koppelmann             cSign ^= 1;
76*ddd7feadSBastian Koppelmann         }
77*ddd7feadSBastian Koppelmann         if (((aExp == 0xff) || (bExp == 0xff)) && (cExp == 0xff)) {
78*ddd7feadSBastian Koppelmann             if (aSign ^ bSign ^ cSign) {
79*ddd7feadSBastian Koppelmann                 return ADD_NAN;
80*ddd7feadSBastian Koppelmann             }
81*ddd7feadSBastian Koppelmann         }
82*ddd7feadSBastian Koppelmann     }
83*ddd7feadSBastian Koppelmann 
84*ddd7feadSBastian Koppelmann     return result;
85*ddd7feadSBastian Koppelmann }
86*ddd7feadSBastian Koppelmann 
87baf410dcSBastian Koppelmann static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags)
88996a729fSBastian Koppelmann {
89996a729fSBastian Koppelmann     uint8_t some_excp = 0;
90996a729fSBastian Koppelmann     set_float_exception_flags(0, &env->fp_status);
91996a729fSBastian Koppelmann 
92996a729fSBastian Koppelmann     if (flags & float_flag_invalid) {
93996a729fSBastian Koppelmann         env->FPU_FI = 1 << 31;
94996a729fSBastian Koppelmann         some_excp = 1;
95996a729fSBastian Koppelmann     }
96996a729fSBastian Koppelmann 
97996a729fSBastian Koppelmann     if (flags & float_flag_overflow) {
98996a729fSBastian Koppelmann         env->FPU_FV = 1 << 31;
99996a729fSBastian Koppelmann         some_excp = 1;
100996a729fSBastian Koppelmann     }
101996a729fSBastian Koppelmann 
102996a729fSBastian Koppelmann     if (flags & float_flag_underflow || flags & float_flag_output_denormal) {
103996a729fSBastian Koppelmann         env->FPU_FU = 1 << 31;
104996a729fSBastian Koppelmann         some_excp = 1;
105996a729fSBastian Koppelmann     }
106996a729fSBastian Koppelmann 
107996a729fSBastian Koppelmann     if (flags & float_flag_divbyzero) {
108996a729fSBastian Koppelmann         env->FPU_FZ = 1 << 31;
109996a729fSBastian Koppelmann         some_excp = 1;
110996a729fSBastian Koppelmann     }
111996a729fSBastian Koppelmann 
112996a729fSBastian Koppelmann     if (flags & float_flag_inexact || flags & float_flag_output_denormal) {
113996a729fSBastian Koppelmann         env->PSW |= 1 << 26;
114996a729fSBastian Koppelmann         some_excp = 1;
115996a729fSBastian Koppelmann     }
116996a729fSBastian Koppelmann 
117996a729fSBastian Koppelmann     env->FPU_FS = some_excp;
118996a729fSBastian Koppelmann }
119baf410dcSBastian Koppelmann 
120baf410dcSBastian Koppelmann #define FADD_SUB(op)                                                           \
121baf410dcSBastian Koppelmann uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2)          \
122baf410dcSBastian Koppelmann {                                                                              \
123baf410dcSBastian Koppelmann     float32 arg1 = make_float32(r1);                                           \
124baf410dcSBastian Koppelmann     float32 arg2 = make_float32(r2);                                           \
125baf410dcSBastian Koppelmann     uint32_t flags;                                                            \
126baf410dcSBastian Koppelmann     float32 f_result;                                                          \
127baf410dcSBastian Koppelmann                                                                                \
128baf410dcSBastian Koppelmann     f_result = float32_##op(arg2, arg1, &env->fp_status);                      \
129baf410dcSBastian Koppelmann     flags = f_get_excp_flags(env);                                             \
130baf410dcSBastian Koppelmann     if (flags) {                                                               \
131baf410dcSBastian Koppelmann         /* If the output is a NaN, but the inputs aren't,                      \
132baf410dcSBastian Koppelmann            we return a unique value.  */                                       \
133baf410dcSBastian Koppelmann         if ((flags & float_flag_invalid)                                       \
134baf410dcSBastian Koppelmann             && !float32_is_any_nan(arg1)                                       \
135baf410dcSBastian Koppelmann             && !float32_is_any_nan(arg2)) {                                    \
136baf410dcSBastian Koppelmann             f_result = ADD_NAN;                                                \
137baf410dcSBastian Koppelmann         }                                                                      \
138baf410dcSBastian Koppelmann         f_update_psw_flags(env, flags);                                        \
139baf410dcSBastian Koppelmann     } else {                                                                   \
140baf410dcSBastian Koppelmann         env->FPU_FS = 0;                                                       \
141baf410dcSBastian Koppelmann     }                                                                          \
142baf410dcSBastian Koppelmann     return (uint32_t)f_result;                                                 \
143baf410dcSBastian Koppelmann }
144baf410dcSBastian Koppelmann FADD_SUB(add)
145baf410dcSBastian Koppelmann FADD_SUB(sub)
146daab3f7fSBastian Koppelmann 
147daab3f7fSBastian Koppelmann uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
148daab3f7fSBastian Koppelmann {
149daab3f7fSBastian Koppelmann     uint32_t flags;
150daab3f7fSBastian Koppelmann     float32 arg1 = make_float32(r1);
151daab3f7fSBastian Koppelmann     float32 arg2 = make_float32(r2);
152daab3f7fSBastian Koppelmann     float32 f_result;
153daab3f7fSBastian Koppelmann 
154daab3f7fSBastian Koppelmann     f_result = float32_mul(arg1, arg2, &env->fp_status);
155daab3f7fSBastian Koppelmann 
156daab3f7fSBastian Koppelmann     flags = f_get_excp_flags(env);
157daab3f7fSBastian Koppelmann     if (flags) {
158daab3f7fSBastian Koppelmann         /* If the output is a NaN, but the inputs aren't,
159daab3f7fSBastian Koppelmann            we return a unique value.  */
160daab3f7fSBastian Koppelmann         if ((flags & float_flag_invalid)
161daab3f7fSBastian Koppelmann             && !float32_is_any_nan(arg1)
162daab3f7fSBastian Koppelmann             && !float32_is_any_nan(arg2)) {
163daab3f7fSBastian Koppelmann                 f_result = MUL_NAN;
164daab3f7fSBastian Koppelmann         }
165daab3f7fSBastian Koppelmann         f_update_psw_flags(env, flags);
166daab3f7fSBastian Koppelmann     } else {
167daab3f7fSBastian Koppelmann         env->FPU_FS = 0;
168daab3f7fSBastian Koppelmann     }
169daab3f7fSBastian Koppelmann     return (uint32_t)f_result;
170daab3f7fSBastian Koppelmann 
171daab3f7fSBastian Koppelmann }
172446ee5b2SBastian Koppelmann 
173446ee5b2SBastian Koppelmann uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
174446ee5b2SBastian Koppelmann {
175446ee5b2SBastian Koppelmann     uint32_t flags;
176446ee5b2SBastian Koppelmann     float32 arg1 = make_float32(r1);
177446ee5b2SBastian Koppelmann     float32 arg2 = make_float32(r2);
178446ee5b2SBastian Koppelmann     float32 f_result;
179446ee5b2SBastian Koppelmann 
180446ee5b2SBastian Koppelmann     f_result = float32_div(arg1, arg2 , &env->fp_status);
181446ee5b2SBastian Koppelmann 
182446ee5b2SBastian Koppelmann     flags = f_get_excp_flags(env);
183446ee5b2SBastian Koppelmann     if (flags) {
184446ee5b2SBastian Koppelmann         /* If the output is a NaN, but the inputs aren't,
185446ee5b2SBastian Koppelmann            we return a unique value.  */
186446ee5b2SBastian Koppelmann         if ((flags & float_flag_invalid)
187446ee5b2SBastian Koppelmann             && !float32_is_any_nan(arg1)
188446ee5b2SBastian Koppelmann             && !float32_is_any_nan(arg2)) {
189446ee5b2SBastian Koppelmann                 f_result = DIV_NAN;
190446ee5b2SBastian Koppelmann         }
191446ee5b2SBastian Koppelmann         f_update_psw_flags(env, flags);
192446ee5b2SBastian Koppelmann     } else {
193446ee5b2SBastian Koppelmann         env->FPU_FS = 0;
194446ee5b2SBastian Koppelmann     }
195446ee5b2SBastian Koppelmann 
196446ee5b2SBastian Koppelmann     return (uint32_t)f_result;
197446ee5b2SBastian Koppelmann }
198743cd09dSBastian Koppelmann 
199*ddd7feadSBastian Koppelmann uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1,
200*ddd7feadSBastian Koppelmann                       uint32_t r2, uint32_t r3)
201*ddd7feadSBastian Koppelmann {
202*ddd7feadSBastian Koppelmann     uint32_t flags;
203*ddd7feadSBastian Koppelmann     float32 arg1 = make_float32(r1);
204*ddd7feadSBastian Koppelmann     float32 arg2 = make_float32(r2);
205*ddd7feadSBastian Koppelmann     float32 arg3 = make_float32(r3);
206*ddd7feadSBastian Koppelmann     float32 f_result;
207*ddd7feadSBastian Koppelmann 
208*ddd7feadSBastian Koppelmann     f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status);
209*ddd7feadSBastian Koppelmann 
210*ddd7feadSBastian Koppelmann     flags = f_get_excp_flags(env);
211*ddd7feadSBastian Koppelmann     if (flags) {
212*ddd7feadSBastian Koppelmann         if (flags & float_flag_invalid) {
213*ddd7feadSBastian Koppelmann             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
214*ddd7feadSBastian Koppelmann             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
215*ddd7feadSBastian Koppelmann             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
216*ddd7feadSBastian Koppelmann             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0);
217*ddd7feadSBastian Koppelmann         }
218*ddd7feadSBastian Koppelmann         f_update_psw_flags(env, flags);
219*ddd7feadSBastian Koppelmann     } else {
220*ddd7feadSBastian Koppelmann         env->FPU_FS = 0;
221*ddd7feadSBastian Koppelmann     }
222*ddd7feadSBastian Koppelmann     return (uint32_t)f_result;
223*ddd7feadSBastian Koppelmann }
224*ddd7feadSBastian Koppelmann 
225*ddd7feadSBastian Koppelmann uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1,
226*ddd7feadSBastian Koppelmann                       uint32_t r2, uint32_t r3)
227*ddd7feadSBastian Koppelmann {
228*ddd7feadSBastian Koppelmann     uint32_t flags;
229*ddd7feadSBastian Koppelmann     float32 arg1 = make_float32(r1);
230*ddd7feadSBastian Koppelmann     float32 arg2 = make_float32(r2);
231*ddd7feadSBastian Koppelmann     float32 arg3 = make_float32(r3);
232*ddd7feadSBastian Koppelmann     float32 f_result;
233*ddd7feadSBastian Koppelmann 
234*ddd7feadSBastian Koppelmann     f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product,
235*ddd7feadSBastian Koppelmann                               &env->fp_status);
236*ddd7feadSBastian Koppelmann 
237*ddd7feadSBastian Koppelmann     flags = f_get_excp_flags(env);
238*ddd7feadSBastian Koppelmann     if (flags) {
239*ddd7feadSBastian Koppelmann         if (flags & float_flag_invalid) {
240*ddd7feadSBastian Koppelmann             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
241*ddd7feadSBastian Koppelmann             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
242*ddd7feadSBastian Koppelmann             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
243*ddd7feadSBastian Koppelmann 
244*ddd7feadSBastian Koppelmann             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1);
245*ddd7feadSBastian Koppelmann         }
246*ddd7feadSBastian Koppelmann         f_update_psw_flags(env, flags);
247*ddd7feadSBastian Koppelmann     } else {
248*ddd7feadSBastian Koppelmann         env->FPU_FS = 0;
249*ddd7feadSBastian Koppelmann     }
250*ddd7feadSBastian Koppelmann     return (uint32_t)f_result;
251*ddd7feadSBastian Koppelmann }
252*ddd7feadSBastian Koppelmann 
253743cd09dSBastian Koppelmann uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
254743cd09dSBastian Koppelmann {
255743cd09dSBastian Koppelmann     uint32_t result, flags;
256743cd09dSBastian Koppelmann     float32 arg1 = make_float32(r1);
257743cd09dSBastian Koppelmann     float32 arg2 = make_float32(r2);
258743cd09dSBastian Koppelmann 
259743cd09dSBastian Koppelmann     set_flush_inputs_to_zero(0, &env->fp_status);
260743cd09dSBastian Koppelmann 
261743cd09dSBastian Koppelmann     result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1);
262743cd09dSBastian Koppelmann     result |= f_is_denormal(arg1) << 4;
263743cd09dSBastian Koppelmann     result |= f_is_denormal(arg2) << 5;
264743cd09dSBastian Koppelmann 
265743cd09dSBastian Koppelmann     flags = f_get_excp_flags(env);
266743cd09dSBastian Koppelmann     if (flags) {
267743cd09dSBastian Koppelmann         f_update_psw_flags(env, flags);
268743cd09dSBastian Koppelmann     } else {
269743cd09dSBastian Koppelmann         env->FPU_FS = 0;
270743cd09dSBastian Koppelmann     }
271743cd09dSBastian Koppelmann 
272743cd09dSBastian Koppelmann     set_flush_inputs_to_zero(1, &env->fp_status);
273743cd09dSBastian Koppelmann     return result;
274743cd09dSBastian Koppelmann }
2750d4c3b80SBastian Koppelmann 
2760d4c3b80SBastian Koppelmann uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg)
2770d4c3b80SBastian Koppelmann {
2780d4c3b80SBastian Koppelmann     float32 f_arg = make_float32(arg);
2790d4c3b80SBastian Koppelmann     int32_t result, flags;
2800d4c3b80SBastian Koppelmann 
2810d4c3b80SBastian Koppelmann     result = float32_to_int32(f_arg, &env->fp_status);
2820d4c3b80SBastian Koppelmann 
2830d4c3b80SBastian Koppelmann     flags = f_get_excp_flags(env);
2840d4c3b80SBastian Koppelmann     if (flags) {
2850d4c3b80SBastian Koppelmann         if (float32_is_any_nan(f_arg)) {
2860d4c3b80SBastian Koppelmann             result = 0;
2870d4c3b80SBastian Koppelmann         }
2880d4c3b80SBastian Koppelmann         f_update_psw_flags(env, flags);
2890d4c3b80SBastian Koppelmann     } else {
2900d4c3b80SBastian Koppelmann         env->FPU_FS = 0;
2910d4c3b80SBastian Koppelmann     }
2920d4c3b80SBastian Koppelmann     return (uint32_t)result;
2930d4c3b80SBastian Koppelmann }
2940d4c3b80SBastian Koppelmann 
2950d4c3b80SBastian Koppelmann uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg)
2960d4c3b80SBastian Koppelmann {
2970d4c3b80SBastian Koppelmann     float32 f_result;
2980d4c3b80SBastian Koppelmann     uint32_t flags;
2990d4c3b80SBastian Koppelmann     f_result = int32_to_float32(arg, &env->fp_status);
3000d4c3b80SBastian Koppelmann 
3010d4c3b80SBastian Koppelmann     flags = f_get_excp_flags(env);
3020d4c3b80SBastian Koppelmann     if (flags) {
3030d4c3b80SBastian Koppelmann         f_update_psw_flags(env, flags);
3040d4c3b80SBastian Koppelmann     } else {
3050d4c3b80SBastian Koppelmann         env->FPU_FS = 0;
3060d4c3b80SBastian Koppelmann     }
3070d4c3b80SBastian Koppelmann     return (uint32_t)f_result;
3080d4c3b80SBastian Koppelmann }
3098f75983dSBastian Koppelmann 
3108f75983dSBastian Koppelmann uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg)
3118f75983dSBastian Koppelmann {
3128f75983dSBastian Koppelmann     float32 f_arg = make_float32(arg);
3138f75983dSBastian Koppelmann     uint32_t result;
3148f75983dSBastian Koppelmann     int32_t flags;
3158f75983dSBastian Koppelmann 
3168f75983dSBastian Koppelmann     result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status);
3178f75983dSBastian Koppelmann 
3188f75983dSBastian Koppelmann     flags = f_get_excp_flags(env);
3198f75983dSBastian Koppelmann     if (flags & float_flag_invalid) {
3208f75983dSBastian Koppelmann         flags &= ~float_flag_inexact;
3218f75983dSBastian Koppelmann         if (float32_is_any_nan(f_arg)) {
3228f75983dSBastian Koppelmann             result = 0;
3238f75983dSBastian Koppelmann         }
3248f75983dSBastian Koppelmann     } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) {
3258f75983dSBastian Koppelmann         flags = float_flag_invalid;
3268f75983dSBastian Koppelmann         result = 0;
3278f75983dSBastian Koppelmann     }
3288f75983dSBastian Koppelmann 
3298f75983dSBastian Koppelmann     if (flags) {
3308f75983dSBastian Koppelmann         f_update_psw_flags(env, flags);
3318f75983dSBastian Koppelmann     } else {
3328f75983dSBastian Koppelmann         env->FPU_FS = 0;
3338f75983dSBastian Koppelmann     }
3348f75983dSBastian Koppelmann     return result;
3358f75983dSBastian Koppelmann }
336