xref: /openbmc/qemu/target/xtensa/fpu_helper.c (revision 2df1eb27)
1 /*
2  * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the Open Source and Linux Lab nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "qemu/osdep.h"
29 #include "qemu/log.h"
30 #include "cpu.h"
31 #include "exec/helper-proto.h"
32 #include "qemu/host-utils.h"
33 #include "exec/exec-all.h"
34 #include "fpu/softfloat.h"
35 
36 enum {
37     XTENSA_FP_I = 0x1,
38     XTENSA_FP_U = 0x2,
39     XTENSA_FP_O = 0x4,
40     XTENSA_FP_Z = 0x8,
41     XTENSA_FP_V = 0x10,
42 };
43 
44 enum {
45     XTENSA_FCR_FLAGS_SHIFT = 2,
46     XTENSA_FSR_FLAGS_SHIFT = 7,
47 };
48 
49 static const struct {
50     uint32_t xtensa_fp_flag;
51     int softfloat_fp_flag;
52 } xtensa_fp_flag_map[] = {
53     { XTENSA_FP_I, float_flag_inexact, },
54     { XTENSA_FP_U, float_flag_underflow, },
55     { XTENSA_FP_O, float_flag_overflow, },
56     { XTENSA_FP_Z, float_flag_divbyzero, },
57     { XTENSA_FP_V, float_flag_invalid, },
58 };
59 
60 void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v)
61 {
62     static const int rounding_mode[] = {
63         float_round_nearest_even,
64         float_round_to_zero,
65         float_round_up,
66         float_round_down,
67     };
68 
69     env->uregs[FCR] = v & 0xfffff07f;
70     set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status);
71 }
72 
73 void HELPER(wur_fpu_fcr)(CPUXtensaState *env, uint32_t v)
74 {
75     static const int rounding_mode[] = {
76         float_round_nearest_even,
77         float_round_to_zero,
78         float_round_up,
79         float_round_down,
80     };
81 
82     if (v & 0xfffff000) {
83         qemu_log_mask(LOG_GUEST_ERROR,
84                       "MBZ field of FCR is written non-zero: %08x\n", v);
85     }
86     env->uregs[FCR] = v & 0x0000007f;
87     set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status);
88 }
89 
90 void HELPER(wur_fpu_fsr)(CPUXtensaState *env, uint32_t v)
91 {
92     uint32_t flags = v >> XTENSA_FSR_FLAGS_SHIFT;
93     int fef = 0;
94     unsigned i;
95 
96     if (v & 0xfffff000) {
97         qemu_log_mask(LOG_GUEST_ERROR,
98                       "MBZ field of FSR is written non-zero: %08x\n", v);
99     }
100     env->uregs[FSR] = v & 0x00000f80;
101     for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) {
102         if (flags & xtensa_fp_flag_map[i].xtensa_fp_flag) {
103             fef |= xtensa_fp_flag_map[i].softfloat_fp_flag;
104         }
105     }
106     set_float_exception_flags(fef, &env->fp_status);
107 }
108 
109 uint32_t HELPER(rur_fpu_fsr)(CPUXtensaState *env)
110 {
111     uint32_t flags = 0;
112     int fef = get_float_exception_flags(&env->fp_status);
113     unsigned i;
114 
115     for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) {
116         if (fef & xtensa_fp_flag_map[i].softfloat_fp_flag) {
117             flags |= xtensa_fp_flag_map[i].xtensa_fp_flag;
118         }
119     }
120     env->uregs[FSR] = flags << XTENSA_FSR_FLAGS_SHIFT;
121     return flags << XTENSA_FSR_FLAGS_SHIFT;
122 }
123 
124 float64 HELPER(abs_d)(float64 v)
125 {
126     return float64_abs(v);
127 }
128 
129 float32 HELPER(abs_s)(float32 v)
130 {
131     return float32_abs(v);
132 }
133 
134 float64 HELPER(neg_d)(float64 v)
135 {
136     return float64_chs(v);
137 }
138 
139 float32 HELPER(neg_s)(float32 v)
140 {
141     return float32_chs(v);
142 }
143 
144 float32 HELPER(fpu2k_add_s)(CPUXtensaState *env, float32 a, float32 b)
145 {
146     return float32_add(a, b, &env->fp_status);
147 }
148 
149 float32 HELPER(fpu2k_sub_s)(CPUXtensaState *env, float32 a, float32 b)
150 {
151     return float32_sub(a, b, &env->fp_status);
152 }
153 
154 float32 HELPER(fpu2k_mul_s)(CPUXtensaState *env, float32 a, float32 b)
155 {
156     return float32_mul(a, b, &env->fp_status);
157 }
158 
159 float32 HELPER(fpu2k_madd_s)(CPUXtensaState *env,
160                              float32 a, float32 b, float32 c)
161 {
162     return float32_muladd(b, c, a, 0, &env->fp_status);
163 }
164 
165 float32 HELPER(fpu2k_msub_s)(CPUXtensaState *env,
166                              float32 a, float32 b, float32 c)
167 {
168     return float32_muladd(b, c, a, float_muladd_negate_product,
169                           &env->fp_status);
170 }
171 
172 float64 HELPER(add_d)(CPUXtensaState *env, float64 a, float64 b)
173 {
174     set_use_first_nan(true, &env->fp_status);
175     return float64_add(a, b, &env->fp_status);
176 }
177 
178 float32 HELPER(add_s)(CPUXtensaState *env, float32 a, float32 b)
179 {
180     set_use_first_nan(env->config->use_first_nan, &env->fp_status);
181     return float32_add(a, b, &env->fp_status);
182 }
183 
184 float64 HELPER(sub_d)(CPUXtensaState *env, float64 a, float64 b)
185 {
186     set_use_first_nan(true, &env->fp_status);
187     return float64_sub(a, b, &env->fp_status);
188 }
189 
190 float32 HELPER(sub_s)(CPUXtensaState *env, float32 a, float32 b)
191 {
192     set_use_first_nan(env->config->use_first_nan, &env->fp_status);
193     return float32_sub(a, b, &env->fp_status);
194 }
195 
196 float64 HELPER(mul_d)(CPUXtensaState *env, float64 a, float64 b)
197 {
198     set_use_first_nan(true, &env->fp_status);
199     return float64_mul(a, b, &env->fp_status);
200 }
201 
202 float32 HELPER(mul_s)(CPUXtensaState *env, float32 a, float32 b)
203 {
204     set_use_first_nan(env->config->use_first_nan, &env->fp_status);
205     return float32_mul(a, b, &env->fp_status);
206 }
207 
208 float64 HELPER(madd_d)(CPUXtensaState *env, float64 a, float64 b, float64 c)
209 {
210     set_use_first_nan(env->config->use_first_nan, &env->fp_status);
211     return float64_muladd(b, c, a, 0, &env->fp_status);
212 }
213 
214 float32 HELPER(madd_s)(CPUXtensaState *env, float32 a, float32 b, float32 c)
215 {
216     set_use_first_nan(env->config->use_first_nan, &env->fp_status);
217     return float32_muladd(b, c, a, 0, &env->fp_status);
218 }
219 
220 float64 HELPER(msub_d)(CPUXtensaState *env, float64 a, float64 b, float64 c)
221 {
222     set_use_first_nan(env->config->use_first_nan, &env->fp_status);
223     return float64_muladd(b, c, a, float_muladd_negate_product,
224                           &env->fp_status);
225 }
226 
227 float32 HELPER(msub_s)(CPUXtensaState *env, float32 a, float32 b, float32 c)
228 {
229     set_use_first_nan(env->config->use_first_nan, &env->fp_status);
230     return float32_muladd(b, c, a, float_muladd_negate_product,
231                           &env->fp_status);
232 }
233 
234 float64 HELPER(mkdadj_d)(CPUXtensaState *env, float64 a, float64 b)
235 {
236     set_use_first_nan(true, &env->fp_status);
237     return float64_div(b, a, &env->fp_status);
238 }
239 
240 float32 HELPER(mkdadj_s)(CPUXtensaState *env, float32 a, float32 b)
241 {
242     set_use_first_nan(env->config->use_first_nan, &env->fp_status);
243     return float32_div(b, a, &env->fp_status);
244 }
245 
246 float64 HELPER(mksadj_d)(CPUXtensaState *env, float64 v)
247 {
248     set_use_first_nan(true, &env->fp_status);
249     return float64_sqrt(v, &env->fp_status);
250 }
251 
252 float32 HELPER(mksadj_s)(CPUXtensaState *env, float32 v)
253 {
254     set_use_first_nan(env->config->use_first_nan, &env->fp_status);
255     return float32_sqrt(v, &env->fp_status);
256 }
257 
258 uint32_t HELPER(ftoi_d)(CPUXtensaState *env, float64 v,
259                         uint32_t rounding_mode, uint32_t scale)
260 {
261     float_status fp_status = env->fp_status;
262     uint32_t res;
263 
264     set_float_rounding_mode(rounding_mode, &fp_status);
265     res = float64_to_int32(float64_scalbn(v, scale, &fp_status), &fp_status);
266     set_float_exception_flags(get_float_exception_flags(&fp_status),
267                               &env->fp_status);
268     return res;
269 }
270 
271 uint32_t HELPER(ftoi_s)(CPUXtensaState *env, float32 v,
272                         uint32_t rounding_mode, uint32_t scale)
273 {
274     float_status fp_status = env->fp_status;
275     uint32_t res;
276 
277     set_float_rounding_mode(rounding_mode, &fp_status);
278     res = float32_to_int32(float32_scalbn(v, scale, &fp_status), &fp_status);
279     set_float_exception_flags(get_float_exception_flags(&fp_status),
280                               &env->fp_status);
281     return res;
282 }
283 
284 uint32_t HELPER(ftoui_d)(CPUXtensaState *env, float64 v,
285                          uint32_t rounding_mode, uint32_t scale)
286 {
287     float_status fp_status = env->fp_status;
288     float64 res;
289     uint32_t rv;
290 
291     set_float_rounding_mode(rounding_mode, &fp_status);
292 
293     res = float64_scalbn(v, scale, &fp_status);
294 
295     if (float64_is_neg(v) && !float64_is_any_nan(v)) {
296         set_float_exception_flags(float_flag_invalid, &fp_status);
297         rv = float64_to_int32(res, &fp_status);
298     } else {
299         rv = float64_to_uint32(res, &fp_status);
300     }
301     set_float_exception_flags(get_float_exception_flags(&fp_status),
302                               &env->fp_status);
303     return rv;
304 }
305 
306 uint32_t HELPER(ftoui_s)(CPUXtensaState *env, float32 v,
307                          uint32_t rounding_mode, uint32_t scale)
308 {
309     float_status fp_status = env->fp_status;
310     float32 res;
311     uint32_t rv;
312 
313     set_float_rounding_mode(rounding_mode, &fp_status);
314 
315     res = float32_scalbn(v, scale, &fp_status);
316 
317     if (float32_is_neg(v) && !float32_is_any_nan(v)) {
318         rv = float32_to_int32(res, &fp_status);
319         if (rv) {
320             set_float_exception_flags(float_flag_invalid, &fp_status);
321         }
322     } else {
323         rv = float32_to_uint32(res, &fp_status);
324     }
325     set_float_exception_flags(get_float_exception_flags(&fp_status),
326                               &env->fp_status);
327     return rv;
328 }
329 
330 float64 HELPER(itof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale)
331 {
332     return float64_scalbn(int32_to_float64(v, &env->fp_status),
333                           (int32_t)scale, &env->fp_status);
334 }
335 
336 float32 HELPER(itof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale)
337 {
338     return float32_scalbn(int32_to_float32(v, &env->fp_status),
339                           (int32_t)scale, &env->fp_status);
340 }
341 
342 float64 HELPER(uitof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale)
343 {
344     return float64_scalbn(uint32_to_float64(v, &env->fp_status),
345                           (int32_t)scale, &env->fp_status);
346 }
347 
348 float32 HELPER(uitof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale)
349 {
350     return float32_scalbn(uint32_to_float32(v, &env->fp_status),
351                           (int32_t)scale, &env->fp_status);
352 }
353 
354 float64 HELPER(cvtd_s)(CPUXtensaState *env, float32 v)
355 {
356     return float32_to_float64(v, &env->fp_status);
357 }
358 
359 float32 HELPER(cvts_d)(CPUXtensaState *env, float64 v)
360 {
361     return float64_to_float32(v, &env->fp_status);
362 }
363 
364 uint32_t HELPER(un_d)(CPUXtensaState *env, float64 a, float64 b)
365 {
366     return float64_unordered_quiet(a, b, &env->fp_status);
367 }
368 
369 uint32_t HELPER(un_s)(CPUXtensaState *env, float32 a, float32 b)
370 {
371     return float32_unordered_quiet(a, b, &env->fp_status);
372 }
373 
374 uint32_t HELPER(oeq_d)(CPUXtensaState *env, float64 a, float64 b)
375 {
376     return float64_eq_quiet(a, b, &env->fp_status);
377 }
378 
379 uint32_t HELPER(oeq_s)(CPUXtensaState *env, float32 a, float32 b)
380 {
381     return float32_eq_quiet(a, b, &env->fp_status);
382 }
383 
384 uint32_t HELPER(ueq_d)(CPUXtensaState *env, float64 a, float64 b)
385 {
386     FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
387 
388     return v == float_relation_equal ||
389            v == float_relation_unordered;
390 }
391 
392 uint32_t HELPER(ueq_s)(CPUXtensaState *env, float32 a, float32 b)
393 {
394     FloatRelation v = float32_compare_quiet(a, b, &env->fp_status);
395 
396     return v == float_relation_equal ||
397            v == float_relation_unordered;
398 }
399 
400 uint32_t HELPER(olt_d)(CPUXtensaState *env, float64 a, float64 b)
401 {
402     return float64_lt(a, b, &env->fp_status);
403 }
404 
405 uint32_t HELPER(olt_s)(CPUXtensaState *env, float32 a, float32 b)
406 {
407     return float32_lt(a, b, &env->fp_status);
408 }
409 
410 uint32_t HELPER(ult_d)(CPUXtensaState *env, float64 a, float64 b)
411 {
412     FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
413 
414     return v == float_relation_less ||
415            v == float_relation_unordered;
416 }
417 
418 uint32_t HELPER(ult_s)(CPUXtensaState *env, float32 a, float32 b)
419 {
420     FloatRelation v = float32_compare_quiet(a, b, &env->fp_status);
421 
422     return v == float_relation_less ||
423            v == float_relation_unordered;
424 }
425 
426 uint32_t HELPER(ole_d)(CPUXtensaState *env, float64 a, float64 b)
427 {
428     return float64_le(a, b, &env->fp_status);
429 }
430 
431 uint32_t HELPER(ole_s)(CPUXtensaState *env, float32 a, float32 b)
432 {
433     return float32_le(a, b, &env->fp_status);
434 }
435 
436 uint32_t HELPER(ule_d)(CPUXtensaState *env, float64 a, float64 b)
437 {
438     FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
439 
440     return v != float_relation_greater;
441 }
442 
443 uint32_t HELPER(ule_s)(CPUXtensaState *env, float32 a, float32 b)
444 {
445     FloatRelation v = float32_compare_quiet(a, b, &env->fp_status);
446 
447     return v != float_relation_greater;
448 }
449