xref: /openbmc/qemu/target/xtensa/fpu_helper.c (revision 8d988eb4)
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 
xtensa_use_first_nan(CPUXtensaState * env,bool use_first)60 void xtensa_use_first_nan(CPUXtensaState *env, bool use_first)
61 {
62     set_use_first_nan(use_first, &env->fp_status);
63     set_float_2nan_prop_rule(use_first ? float_2nan_prop_ab : float_2nan_prop_ba,
64                              &env->fp_status);
65 }
66 
HELPER(wur_fpu2k_fcr)67 void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v)
68 {
69     static const int rounding_mode[] = {
70         float_round_nearest_even,
71         float_round_to_zero,
72         float_round_up,
73         float_round_down,
74     };
75 
76     env->uregs[FCR] = v & 0xfffff07f;
77     set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status);
78 }
79 
HELPER(wur_fpu_fcr)80 void HELPER(wur_fpu_fcr)(CPUXtensaState *env, uint32_t v)
81 {
82     static const int rounding_mode[] = {
83         float_round_nearest_even,
84         float_round_to_zero,
85         float_round_up,
86         float_round_down,
87     };
88 
89     if (v & 0xfffff000) {
90         qemu_log_mask(LOG_GUEST_ERROR,
91                       "MBZ field of FCR is written non-zero: %08x\n", v);
92     }
93     env->uregs[FCR] = v & 0x0000007f;
94     set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status);
95 }
96 
HELPER(wur_fpu_fsr)97 void HELPER(wur_fpu_fsr)(CPUXtensaState *env, uint32_t v)
98 {
99     uint32_t flags = v >> XTENSA_FSR_FLAGS_SHIFT;
100     int fef = 0;
101     unsigned i;
102 
103     if (v & 0xfffff000) {
104         qemu_log_mask(LOG_GUEST_ERROR,
105                       "MBZ field of FSR is written non-zero: %08x\n", v);
106     }
107     env->uregs[FSR] = v & 0x00000f80;
108     for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) {
109         if (flags & xtensa_fp_flag_map[i].xtensa_fp_flag) {
110             fef |= xtensa_fp_flag_map[i].softfloat_fp_flag;
111         }
112     }
113     set_float_exception_flags(fef, &env->fp_status);
114 }
115 
HELPER(rur_fpu_fsr)116 uint32_t HELPER(rur_fpu_fsr)(CPUXtensaState *env)
117 {
118     uint32_t flags = 0;
119     int fef = get_float_exception_flags(&env->fp_status);
120     unsigned i;
121 
122     for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) {
123         if (fef & xtensa_fp_flag_map[i].softfloat_fp_flag) {
124             flags |= xtensa_fp_flag_map[i].xtensa_fp_flag;
125         }
126     }
127     env->uregs[FSR] = flags << XTENSA_FSR_FLAGS_SHIFT;
128     return flags << XTENSA_FSR_FLAGS_SHIFT;
129 }
130 
HELPER(abs_d)131 float64 HELPER(abs_d)(float64 v)
132 {
133     return float64_abs(v);
134 }
135 
HELPER(abs_s)136 float32 HELPER(abs_s)(float32 v)
137 {
138     return float32_abs(v);
139 }
140 
HELPER(neg_d)141 float64 HELPER(neg_d)(float64 v)
142 {
143     return float64_chs(v);
144 }
145 
HELPER(neg_s)146 float32 HELPER(neg_s)(float32 v)
147 {
148     return float32_chs(v);
149 }
150 
HELPER(fpu2k_add_s)151 float32 HELPER(fpu2k_add_s)(CPUXtensaState *env, float32 a, float32 b)
152 {
153     return float32_add(a, b, &env->fp_status);
154 }
155 
HELPER(fpu2k_sub_s)156 float32 HELPER(fpu2k_sub_s)(CPUXtensaState *env, float32 a, float32 b)
157 {
158     return float32_sub(a, b, &env->fp_status);
159 }
160 
HELPER(fpu2k_mul_s)161 float32 HELPER(fpu2k_mul_s)(CPUXtensaState *env, float32 a, float32 b)
162 {
163     return float32_mul(a, b, &env->fp_status);
164 }
165 
HELPER(fpu2k_madd_s)166 float32 HELPER(fpu2k_madd_s)(CPUXtensaState *env,
167                              float32 a, float32 b, float32 c)
168 {
169     return float32_muladd(b, c, a, 0, &env->fp_status);
170 }
171 
HELPER(fpu2k_msub_s)172 float32 HELPER(fpu2k_msub_s)(CPUXtensaState *env,
173                              float32 a, float32 b, float32 c)
174 {
175     return float32_muladd(b, c, a, float_muladd_negate_product,
176                           &env->fp_status);
177 }
178 
HELPER(add_d)179 float64 HELPER(add_d)(CPUXtensaState *env, float64 a, float64 b)
180 {
181     xtensa_use_first_nan(env, true);
182     return float64_add(a, b, &env->fp_status);
183 }
184 
HELPER(add_s)185 float32 HELPER(add_s)(CPUXtensaState *env, float32 a, float32 b)
186 {
187     xtensa_use_first_nan(env, env->config->use_first_nan);
188     return float32_add(a, b, &env->fp_status);
189 }
190 
HELPER(sub_d)191 float64 HELPER(sub_d)(CPUXtensaState *env, float64 a, float64 b)
192 {
193     xtensa_use_first_nan(env, true);
194     return float64_sub(a, b, &env->fp_status);
195 }
196 
HELPER(sub_s)197 float32 HELPER(sub_s)(CPUXtensaState *env, float32 a, float32 b)
198 {
199     xtensa_use_first_nan(env, env->config->use_first_nan);
200     return float32_sub(a, b, &env->fp_status);
201 }
202 
HELPER(mul_d)203 float64 HELPER(mul_d)(CPUXtensaState *env, float64 a, float64 b)
204 {
205     xtensa_use_first_nan(env, true);
206     return float64_mul(a, b, &env->fp_status);
207 }
208 
HELPER(mul_s)209 float32 HELPER(mul_s)(CPUXtensaState *env, float32 a, float32 b)
210 {
211     xtensa_use_first_nan(env, env->config->use_first_nan);
212     return float32_mul(a, b, &env->fp_status);
213 }
214 
HELPER(madd_d)215 float64 HELPER(madd_d)(CPUXtensaState *env, float64 a, float64 b, float64 c)
216 {
217     xtensa_use_first_nan(env, env->config->use_first_nan);
218     return float64_muladd(b, c, a, 0, &env->fp_status);
219 }
220 
HELPER(madd_s)221 float32 HELPER(madd_s)(CPUXtensaState *env, float32 a, float32 b, float32 c)
222 {
223     xtensa_use_first_nan(env, env->config->use_first_nan);
224     return float32_muladd(b, c, a, 0, &env->fp_status);
225 }
226 
HELPER(msub_d)227 float64 HELPER(msub_d)(CPUXtensaState *env, float64 a, float64 b, float64 c)
228 {
229     xtensa_use_first_nan(env, env->config->use_first_nan);
230     return float64_muladd(b, c, a, float_muladd_negate_product,
231                           &env->fp_status);
232 }
233 
HELPER(msub_s)234 float32 HELPER(msub_s)(CPUXtensaState *env, float32 a, float32 b, float32 c)
235 {
236     xtensa_use_first_nan(env, env->config->use_first_nan);
237     return float32_muladd(b, c, a, float_muladd_negate_product,
238                           &env->fp_status);
239 }
240 
HELPER(mkdadj_d)241 float64 HELPER(mkdadj_d)(CPUXtensaState *env, float64 a, float64 b)
242 {
243     xtensa_use_first_nan(env, true);
244     return float64_div(b, a, &env->fp_status);
245 }
246 
HELPER(mkdadj_s)247 float32 HELPER(mkdadj_s)(CPUXtensaState *env, float32 a, float32 b)
248 {
249     xtensa_use_first_nan(env, env->config->use_first_nan);
250     return float32_div(b, a, &env->fp_status);
251 }
252 
HELPER(mksadj_d)253 float64 HELPER(mksadj_d)(CPUXtensaState *env, float64 v)
254 {
255     xtensa_use_first_nan(env, true);
256     return float64_sqrt(v, &env->fp_status);
257 }
258 
HELPER(mksadj_s)259 float32 HELPER(mksadj_s)(CPUXtensaState *env, float32 v)
260 {
261     xtensa_use_first_nan(env, env->config->use_first_nan);
262     return float32_sqrt(v, &env->fp_status);
263 }
264 
HELPER(ftoi_d)265 uint32_t HELPER(ftoi_d)(CPUXtensaState *env, float64 v,
266                         uint32_t rounding_mode, uint32_t scale)
267 {
268     float_status fp_status = env->fp_status;
269     uint32_t res;
270 
271     set_float_rounding_mode(rounding_mode, &fp_status);
272     res = float64_to_int32(float64_scalbn(v, scale, &fp_status), &fp_status);
273     set_float_exception_flags(get_float_exception_flags(&fp_status),
274                               &env->fp_status);
275     return res;
276 }
277 
HELPER(ftoi_s)278 uint32_t HELPER(ftoi_s)(CPUXtensaState *env, float32 v,
279                         uint32_t rounding_mode, uint32_t scale)
280 {
281     float_status fp_status = env->fp_status;
282     uint32_t res;
283 
284     set_float_rounding_mode(rounding_mode, &fp_status);
285     res = float32_to_int32(float32_scalbn(v, scale, &fp_status), &fp_status);
286     set_float_exception_flags(get_float_exception_flags(&fp_status),
287                               &env->fp_status);
288     return res;
289 }
290 
HELPER(ftoui_d)291 uint32_t HELPER(ftoui_d)(CPUXtensaState *env, float64 v,
292                          uint32_t rounding_mode, uint32_t scale)
293 {
294     float_status fp_status = env->fp_status;
295     float64 res;
296     uint32_t rv;
297 
298     set_float_rounding_mode(rounding_mode, &fp_status);
299 
300     res = float64_scalbn(v, scale, &fp_status);
301 
302     if (float64_is_neg(v) && !float64_is_any_nan(v)) {
303         set_float_exception_flags(float_flag_invalid, &fp_status);
304         rv = float64_to_int32(res, &fp_status);
305     } else {
306         rv = float64_to_uint32(res, &fp_status);
307     }
308     set_float_exception_flags(get_float_exception_flags(&fp_status),
309                               &env->fp_status);
310     return rv;
311 }
312 
HELPER(ftoui_s)313 uint32_t HELPER(ftoui_s)(CPUXtensaState *env, float32 v,
314                          uint32_t rounding_mode, uint32_t scale)
315 {
316     float_status fp_status = env->fp_status;
317     float32 res;
318     uint32_t rv;
319 
320     set_float_rounding_mode(rounding_mode, &fp_status);
321 
322     res = float32_scalbn(v, scale, &fp_status);
323 
324     if (float32_is_neg(v) && !float32_is_any_nan(v)) {
325         rv = float32_to_int32(res, &fp_status);
326         if (rv) {
327             set_float_exception_flags(float_flag_invalid, &fp_status);
328         }
329     } else {
330         rv = float32_to_uint32(res, &fp_status);
331     }
332     set_float_exception_flags(get_float_exception_flags(&fp_status),
333                               &env->fp_status);
334     return rv;
335 }
336 
HELPER(itof_d)337 float64 HELPER(itof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale)
338 {
339     return float64_scalbn(int32_to_float64(v, &env->fp_status),
340                           (int32_t)scale, &env->fp_status);
341 }
342 
HELPER(itof_s)343 float32 HELPER(itof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale)
344 {
345     return float32_scalbn(int32_to_float32(v, &env->fp_status),
346                           (int32_t)scale, &env->fp_status);
347 }
348 
HELPER(uitof_d)349 float64 HELPER(uitof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale)
350 {
351     return float64_scalbn(uint32_to_float64(v, &env->fp_status),
352                           (int32_t)scale, &env->fp_status);
353 }
354 
HELPER(uitof_s)355 float32 HELPER(uitof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale)
356 {
357     return float32_scalbn(uint32_to_float32(v, &env->fp_status),
358                           (int32_t)scale, &env->fp_status);
359 }
360 
HELPER(cvtd_s)361 float64 HELPER(cvtd_s)(CPUXtensaState *env, float32 v)
362 {
363     return float32_to_float64(v, &env->fp_status);
364 }
365 
HELPER(cvts_d)366 float32 HELPER(cvts_d)(CPUXtensaState *env, float64 v)
367 {
368     return float64_to_float32(v, &env->fp_status);
369 }
370 
HELPER(un_d)371 uint32_t HELPER(un_d)(CPUXtensaState *env, float64 a, float64 b)
372 {
373     return float64_unordered_quiet(a, b, &env->fp_status);
374 }
375 
HELPER(un_s)376 uint32_t HELPER(un_s)(CPUXtensaState *env, float32 a, float32 b)
377 {
378     return float32_unordered_quiet(a, b, &env->fp_status);
379 }
380 
HELPER(oeq_d)381 uint32_t HELPER(oeq_d)(CPUXtensaState *env, float64 a, float64 b)
382 {
383     return float64_eq_quiet(a, b, &env->fp_status);
384 }
385 
HELPER(oeq_s)386 uint32_t HELPER(oeq_s)(CPUXtensaState *env, float32 a, float32 b)
387 {
388     return float32_eq_quiet(a, b, &env->fp_status);
389 }
390 
HELPER(ueq_d)391 uint32_t HELPER(ueq_d)(CPUXtensaState *env, float64 a, float64 b)
392 {
393     FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
394 
395     return v == float_relation_equal ||
396            v == float_relation_unordered;
397 }
398 
HELPER(ueq_s)399 uint32_t HELPER(ueq_s)(CPUXtensaState *env, float32 a, float32 b)
400 {
401     FloatRelation v = float32_compare_quiet(a, b, &env->fp_status);
402 
403     return v == float_relation_equal ||
404            v == float_relation_unordered;
405 }
406 
HELPER(olt_d)407 uint32_t HELPER(olt_d)(CPUXtensaState *env, float64 a, float64 b)
408 {
409     return float64_lt(a, b, &env->fp_status);
410 }
411 
HELPER(olt_s)412 uint32_t HELPER(olt_s)(CPUXtensaState *env, float32 a, float32 b)
413 {
414     return float32_lt(a, b, &env->fp_status);
415 }
416 
HELPER(ult_d)417 uint32_t HELPER(ult_d)(CPUXtensaState *env, float64 a, float64 b)
418 {
419     FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
420 
421     return v == float_relation_less ||
422            v == float_relation_unordered;
423 }
424 
HELPER(ult_s)425 uint32_t HELPER(ult_s)(CPUXtensaState *env, float32 a, float32 b)
426 {
427     FloatRelation v = float32_compare_quiet(a, b, &env->fp_status);
428 
429     return v == float_relation_less ||
430            v == float_relation_unordered;
431 }
432 
HELPER(ole_d)433 uint32_t HELPER(ole_d)(CPUXtensaState *env, float64 a, float64 b)
434 {
435     return float64_le(a, b, &env->fp_status);
436 }
437 
HELPER(ole_s)438 uint32_t HELPER(ole_s)(CPUXtensaState *env, float32 a, float32 b)
439 {
440     return float32_le(a, b, &env->fp_status);
441 }
442 
HELPER(ule_d)443 uint32_t HELPER(ule_d)(CPUXtensaState *env, float64 a, float64 b)
444 {
445     FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
446 
447     return v != float_relation_greater;
448 }
449 
HELPER(ule_s)450 uint32_t HELPER(ule_s)(CPUXtensaState *env, float32 a, float32 b)
451 {
452     FloatRelation v = float32_compare_quiet(a, b, &env->fp_status);
453 
454     return v != float_relation_greater;
455 }
456