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