xref: /openbmc/qemu/target/tricore/fpu_helper.c (revision 7d405b2f)
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 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 DIV_NAN   0x7fc00008
28 #define MUL_NAN   0x7fc00002
29 #define FPU_FS PSW_USB_C
30 #define FPU_FI PSW_USB_V
31 #define FPU_FV PSW_USB_SV
32 #define FPU_FZ PSW_USB_AV
33 #define FPU_FU PSW_USB_SAV
34 
35 /* we don't care about input_denormal */
36 static inline uint8_t f_get_excp_flags(CPUTriCoreState *env)
37 {
38     return get_float_exception_flags(&env->fp_status)
39            & (float_flag_invalid
40               | float_flag_overflow
41               | float_flag_underflow
42               | float_flag_output_denormal
43               | float_flag_divbyzero
44               | float_flag_inexact);
45 }
46 
47 static inline bool f_is_denormal(float32 arg)
48 {
49     return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg);
50 }
51 
52 static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2,
53                                            float32 arg3, float32 result,
54                                            uint32_t muladd_negate_c)
55 {
56     uint32_t aSign, bSign, cSign;
57     uint32_t aExp, bExp, cExp;
58 
59     if (float32_is_any_nan(arg1) || float32_is_any_nan(arg2) ||
60         float32_is_any_nan(arg3)) {
61         return QUIET_NAN;
62     } else if (float32_is_infinity(arg1) && float32_is_zero(arg2)) {
63         return MUL_NAN;
64     } else if (float32_is_zero(arg1) && float32_is_infinity(arg2)) {
65         return MUL_NAN;
66     } else {
67         aSign = arg1 >> 31;
68         bSign = arg2 >> 31;
69         cSign = arg3 >> 31;
70 
71         aExp = (arg1 >> 23) & 0xff;
72         bExp = (arg2 >> 23) & 0xff;
73         cExp = (arg3 >> 23) & 0xff;
74 
75         if (muladd_negate_c) {
76             cSign ^= 1;
77         }
78         if (((aExp == 0xff) || (bExp == 0xff)) && (cExp == 0xff)) {
79             if (aSign ^ bSign ^ cSign) {
80                 return ADD_NAN;
81             }
82         }
83     }
84 
85     return result;
86 }
87 
88 static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags)
89 {
90     uint8_t some_excp = 0;
91     set_float_exception_flags(0, &env->fp_status);
92 
93     if (flags & float_flag_invalid) {
94         env->FPU_FI = 1 << 31;
95         some_excp = 1;
96     }
97 
98     if (flags & float_flag_overflow) {
99         env->FPU_FV = 1 << 31;
100         some_excp = 1;
101     }
102 
103     if (flags & float_flag_underflow || flags & float_flag_output_denormal) {
104         env->FPU_FU = 1 << 31;
105         some_excp = 1;
106     }
107 
108     if (flags & float_flag_divbyzero) {
109         env->FPU_FZ = 1 << 31;
110         some_excp = 1;
111     }
112 
113     if (flags & float_flag_inexact || flags & float_flag_output_denormal) {
114         env->PSW |= 1 << 26;
115         some_excp = 1;
116     }
117 
118     env->FPU_FS = some_excp;
119 }
120 
121 #define FADD_SUB(op)                                                           \
122 uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2)          \
123 {                                                                              \
124     float32 arg1 = make_float32(r1);                                           \
125     float32 arg2 = make_float32(r2);                                           \
126     uint32_t flags;                                                            \
127     float32 f_result;                                                          \
128                                                                                \
129     f_result = float32_##op(arg2, arg1, &env->fp_status);                      \
130     flags = f_get_excp_flags(env);                                             \
131     if (flags) {                                                               \
132         /* If the output is a NaN, but the inputs aren't,                      \
133            we return a unique value.  */                                       \
134         if ((flags & float_flag_invalid)                                       \
135             && !float32_is_any_nan(arg1)                                       \
136             && !float32_is_any_nan(arg2)) {                                    \
137             f_result = ADD_NAN;                                                \
138         }                                                                      \
139         f_update_psw_flags(env, flags);                                        \
140     } else {                                                                   \
141         env->FPU_FS = 0;                                                       \
142     }                                                                          \
143     return (uint32_t)f_result;                                                 \
144 }
145 FADD_SUB(add)
146 FADD_SUB(sub)
147 
148 uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
149 {
150     uint32_t flags;
151     float32 arg1 = make_float32(r1);
152     float32 arg2 = make_float32(r2);
153     float32 f_result;
154 
155     f_result = float32_mul(arg1, arg2, &env->fp_status);
156 
157     flags = f_get_excp_flags(env);
158     if (flags) {
159         /* If the output is a NaN, but the inputs aren't,
160            we return a unique value.  */
161         if ((flags & float_flag_invalid)
162             && !float32_is_any_nan(arg1)
163             && !float32_is_any_nan(arg2)) {
164                 f_result = MUL_NAN;
165         }
166         f_update_psw_flags(env, flags);
167     } else {
168         env->FPU_FS = 0;
169     }
170     return (uint32_t)f_result;
171 
172 }
173 
174 uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
175 {
176     uint32_t flags;
177     float32 arg1 = make_float32(r1);
178     float32 arg2 = make_float32(r2);
179     float32 f_result;
180 
181     f_result = float32_div(arg1, arg2 , &env->fp_status);
182 
183     flags = f_get_excp_flags(env);
184     if (flags) {
185         /* If the output is a NaN, but the inputs aren't,
186            we return a unique value.  */
187         if ((flags & float_flag_invalid)
188             && !float32_is_any_nan(arg1)
189             && !float32_is_any_nan(arg2)) {
190                 f_result = DIV_NAN;
191         }
192         f_update_psw_flags(env, flags);
193     } else {
194         env->FPU_FS = 0;
195     }
196 
197     return (uint32_t)f_result;
198 }
199 
200 uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1,
201                       uint32_t r2, uint32_t r3)
202 {
203     uint32_t flags;
204     float32 arg1 = make_float32(r1);
205     float32 arg2 = make_float32(r2);
206     float32 arg3 = make_float32(r3);
207     float32 f_result;
208 
209     f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status);
210 
211     flags = f_get_excp_flags(env);
212     if (flags) {
213         if (flags & float_flag_invalid) {
214             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
215             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
216             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
217             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0);
218         }
219         f_update_psw_flags(env, flags);
220     } else {
221         env->FPU_FS = 0;
222     }
223     return (uint32_t)f_result;
224 }
225 
226 uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1,
227                       uint32_t r2, uint32_t r3)
228 {
229     uint32_t flags;
230     float32 arg1 = make_float32(r1);
231     float32 arg2 = make_float32(r2);
232     float32 arg3 = make_float32(r3);
233     float32 f_result;
234 
235     f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product,
236                               &env->fp_status);
237 
238     flags = f_get_excp_flags(env);
239     if (flags) {
240         if (flags & float_flag_invalid) {
241             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
242             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
243             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
244 
245             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1);
246         }
247         f_update_psw_flags(env, flags);
248     } else {
249         env->FPU_FS = 0;
250     }
251     return (uint32_t)f_result;
252 }
253 
254 uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
255 {
256     uint32_t result, flags;
257     float32 arg1 = make_float32(r1);
258     float32 arg2 = make_float32(r2);
259 
260     set_flush_inputs_to_zero(0, &env->fp_status);
261 
262     result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1);
263     result |= f_is_denormal(arg1) << 4;
264     result |= f_is_denormal(arg2) << 5;
265 
266     flags = f_get_excp_flags(env);
267     if (flags) {
268         f_update_psw_flags(env, flags);
269     } else {
270         env->FPU_FS = 0;
271     }
272 
273     set_flush_inputs_to_zero(1, &env->fp_status);
274     return result;
275 }
276 
277 uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg)
278 {
279     float32 f_arg = make_float32(arg);
280     int32_t result, flags;
281 
282     result = float32_to_int32(f_arg, &env->fp_status);
283 
284     flags = f_get_excp_flags(env);
285     if (flags) {
286         if (float32_is_any_nan(f_arg)) {
287             result = 0;
288         }
289         f_update_psw_flags(env, flags);
290     } else {
291         env->FPU_FS = 0;
292     }
293     return (uint32_t)result;
294 }
295 
296 uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg)
297 {
298     float32 f_result;
299     uint32_t flags;
300     f_result = int32_to_float32(arg, &env->fp_status);
301 
302     flags = f_get_excp_flags(env);
303     if (flags) {
304         f_update_psw_flags(env, flags);
305     } else {
306         env->FPU_FS = 0;
307     }
308     return (uint32_t)f_result;
309 }
310 
311 uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg)
312 {
313     float32 f_arg = make_float32(arg);
314     uint32_t result;
315     int32_t flags;
316 
317     result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status);
318 
319     flags = f_get_excp_flags(env);
320     if (flags & float_flag_invalid) {
321         flags &= ~float_flag_inexact;
322         if (float32_is_any_nan(f_arg)) {
323             result = 0;
324         }
325     } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) {
326         flags = float_flag_invalid;
327         result = 0;
328     }
329 
330     if (flags) {
331         f_update_psw_flags(env, flags);
332     } else {
333         env->FPU_FS = 0;
334     }
335     return result;
336 }
337 
338 void helper_updfl(CPUTriCoreState *env, uint32_t arg)
339 {
340     env->FPU_FS =  extract32(arg, 7, 1) & extract32(arg, 15, 1);
341     env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31;
342     env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31;
343     env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31;
344     env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31;
345     /* clear FX and RM */
346     env->PSW &= ~(extract32(arg, 10, 1) << 26);
347     env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26;
348 
349     fpu_set_state(env);
350 }
351