xref: /openbmc/qemu/target/tricore/fpu_helper.c (revision a9ded601)
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 
24 #define QUIET_NAN 0x7fc00000
25 #define ADD_NAN   0x7fc00001
26 #define DIV_NAN   0x7fc00008
27 #define MUL_NAN   0x7fc00002
28 #define FPU_FS PSW_USB_C
29 #define FPU_FI PSW_USB_V
30 #define FPU_FV PSW_USB_SV
31 #define FPU_FZ PSW_USB_AV
32 #define FPU_FU PSW_USB_SAV
33 
34 /* we don't care about input_denormal */
35 static inline uint8_t f_get_excp_flags(CPUTriCoreState *env)
36 {
37     return get_float_exception_flags(&env->fp_status)
38            & (float_flag_invalid
39               | float_flag_overflow
40               | float_flag_underflow
41               | float_flag_output_denormal
42               | float_flag_divbyzero
43               | float_flag_inexact);
44 }
45 
46 static inline bool f_is_denormal(float32 arg)
47 {
48     return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg);
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 uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
174 {
175     uint32_t flags;
176     float32 arg1 = make_float32(r1);
177     float32 arg2 = make_float32(r2);
178     float32 f_result;
179 
180     f_result = float32_div(arg1, arg2 , &env->fp_status);
181 
182     flags = f_get_excp_flags(env);
183     if (flags) {
184         /* If the output is a NaN, but the inputs aren't,
185            we return a unique value.  */
186         if ((flags & float_flag_invalid)
187             && !float32_is_any_nan(arg1)
188             && !float32_is_any_nan(arg2)) {
189                 f_result = DIV_NAN;
190         }
191         f_update_psw_flags(env, flags);
192     } else {
193         env->FPU_FS = 0;
194     }
195 
196     return (uint32_t)f_result;
197 }
198 
199 uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1,
200                       uint32_t r2, uint32_t r3)
201 {
202     uint32_t flags;
203     float32 arg1 = make_float32(r1);
204     float32 arg2 = make_float32(r2);
205     float32 arg3 = make_float32(r3);
206     float32 f_result;
207 
208     f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status);
209 
210     flags = f_get_excp_flags(env);
211     if (flags) {
212         if (flags & float_flag_invalid) {
213             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
214             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
215             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
216             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0);
217         }
218         f_update_psw_flags(env, flags);
219     } else {
220         env->FPU_FS = 0;
221     }
222     return (uint32_t)f_result;
223 }
224 
225 uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1,
226                       uint32_t r2, uint32_t r3)
227 {
228     uint32_t flags;
229     float32 arg1 = make_float32(r1);
230     float32 arg2 = make_float32(r2);
231     float32 arg3 = make_float32(r3);
232     float32 f_result;
233 
234     f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product,
235                               &env->fp_status);
236 
237     flags = f_get_excp_flags(env);
238     if (flags) {
239         if (flags & float_flag_invalid) {
240             arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
241             arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
242             arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
243 
244             f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1);
245         }
246         f_update_psw_flags(env, flags);
247     } else {
248         env->FPU_FS = 0;
249     }
250     return (uint32_t)f_result;
251 }
252 
253 uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
254 {
255     uint32_t result, flags;
256     float32 arg1 = make_float32(r1);
257     float32 arg2 = make_float32(r2);
258 
259     set_flush_inputs_to_zero(0, &env->fp_status);
260 
261     result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1);
262     result |= f_is_denormal(arg1) << 4;
263     result |= f_is_denormal(arg2) << 5;
264 
265     flags = f_get_excp_flags(env);
266     if (flags) {
267         f_update_psw_flags(env, flags);
268     } else {
269         env->FPU_FS = 0;
270     }
271 
272     set_flush_inputs_to_zero(1, &env->fp_status);
273     return result;
274 }
275 
276 uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg)
277 {
278     float32 f_arg = make_float32(arg);
279     int32_t result, flags;
280 
281     result = float32_to_int32(f_arg, &env->fp_status);
282 
283     flags = f_get_excp_flags(env);
284     if (flags) {
285         if (float32_is_any_nan(f_arg)) {
286             result = 0;
287         }
288         f_update_psw_flags(env, flags);
289     } else {
290         env->FPU_FS = 0;
291     }
292     return (uint32_t)result;
293 }
294 
295 uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg)
296 {
297     float32 f_result;
298     uint32_t flags;
299     f_result = int32_to_float32(arg, &env->fp_status);
300 
301     flags = f_get_excp_flags(env);
302     if (flags) {
303         f_update_psw_flags(env, flags);
304     } else {
305         env->FPU_FS = 0;
306     }
307     return (uint32_t)f_result;
308 }
309 
310 uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg)
311 {
312     float32 f_arg = make_float32(arg);
313     uint32_t result;
314     int32_t flags;
315 
316     result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status);
317 
318     flags = f_get_excp_flags(env);
319     if (flags & float_flag_invalid) {
320         flags &= ~float_flag_inexact;
321         if (float32_is_any_nan(f_arg)) {
322             result = 0;
323         }
324     } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) {
325         flags = float_flag_invalid;
326         result = 0;
327     }
328 
329     if (flags) {
330         f_update_psw_flags(env, flags);
331     } else {
332         env->FPU_FS = 0;
333     }
334     return result;
335 }
336 
337 void helper_updfl(CPUTriCoreState *env, uint32_t arg)
338 {
339     env->FPU_FS =  extract32(arg, 7, 1) & extract32(arg, 15, 1);
340     env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31;
341     env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31;
342     env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31;
343     env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31;
344     /* clear FX and RM */
345     env->PSW &= ~(extract32(arg, 10, 1) << 26);
346     env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26;
347 
348     fpu_set_state(env);
349 }
350