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