]>
Commit | Line | Data |
---|---|---|
9483bf27 MF |
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" | |
cd617484 | 29 | #include "qemu/log.h" |
9483bf27 MF |
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 | ||
cfa9f051 MF |
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 | ||
5680f207 | 61 | void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v) |
9483bf27 MF |
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 | ||
cfa9f051 MF |
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 | ||
9483bf27 MF |
130 | float32 HELPER(abs_s)(float32 v) |
131 | { | |
132 | return float32_abs(v); | |
133 | } | |
134 | ||
cfa9f051 MF |
135 | float64 HELPER(neg_d)(float64 v) |
136 | { | |
137 | return float64_chs(v); | |
138 | } | |
139 | ||
9483bf27 MF |
140 | float32 HELPER(neg_s)(float32 v) |
141 | { | |
142 | return float32_chs(v); | |
143 | } | |
144 | ||
5680f207 | 145 | float32 HELPER(fpu2k_add_s)(CPUXtensaState *env, float32 a, float32 b) |
9483bf27 MF |
146 | { |
147 | return float32_add(a, b, &env->fp_status); | |
148 | } | |
149 | ||
5680f207 | 150 | float32 HELPER(fpu2k_sub_s)(CPUXtensaState *env, float32 a, float32 b) |
9483bf27 MF |
151 | { |
152 | return float32_sub(a, b, &env->fp_status); | |
153 | } | |
154 | ||
5680f207 | 155 | float32 HELPER(fpu2k_mul_s)(CPUXtensaState *env, float32 a, float32 b) |
9483bf27 MF |
156 | { |
157 | return float32_mul(a, b, &env->fp_status); | |
158 | } | |
159 | ||
5680f207 MF |
160 | float32 HELPER(fpu2k_madd_s)(CPUXtensaState *env, |
161 | float32 a, float32 b, float32 c) | |
9483bf27 MF |
162 | { |
163 | return float32_muladd(b, c, a, 0, &env->fp_status); | |
164 | } | |
165 | ||
5680f207 MF |
166 | float32 HELPER(fpu2k_msub_s)(CPUXtensaState *env, |
167 | float32 a, float32 b, float32 c) | |
9483bf27 MF |
168 | { |
169 | return float32_muladd(b, c, a, float_muladd_negate_product, | |
170 | &env->fp_status); | |
171 | } | |
172 | ||
cfa9f051 MF |
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 | ||
f8c61370 MF |
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 | ||
cfa9f051 MF |
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) | |
9483bf27 | 287 | { |
cfa9f051 MF |
288 | float_status fp_status = env->fp_status; |
289 | float64 res; | |
290 | uint32_t rv; | |
9483bf27 MF |
291 | |
292 | set_float_rounding_mode(rounding_mode, &fp_status); | |
cfa9f051 MF |
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; | |
9483bf27 MF |
305 | } |
306 | ||
cfa9f051 MF |
307 | uint32_t HELPER(ftoui_s)(CPUXtensaState *env, float32 v, |
308 | uint32_t rounding_mode, uint32_t scale) | |
9483bf27 | 309 | { |
cfa9f051 | 310 | float_status fp_status = env->fp_status; |
9483bf27 | 311 | float32 res; |
cfa9f051 | 312 | uint32_t rv; |
9483bf27 MF |
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)) { | |
cfa9f051 MF |
319 | rv = float32_to_int32(res, &fp_status); |
320 | if (rv) { | |
321 | set_float_exception_flags(float_flag_invalid, &fp_status); | |
322 | } | |
9483bf27 | 323 | } else { |
cfa9f051 | 324 | rv = float32_to_uint32(res, &fp_status); |
9483bf27 | 325 | } |
cfa9f051 MF |
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); | |
9483bf27 MF |
335 | } |
336 | ||
5680f207 | 337 | float32 HELPER(itof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale) |
9483bf27 MF |
338 | { |
339 | return float32_scalbn(int32_to_float32(v, &env->fp_status), | |
340 | (int32_t)scale, &env->fp_status); | |
341 | } | |
342 | ||
cfa9f051 MF |
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 | ||
5680f207 | 349 | float32 HELPER(uitof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale) |
9483bf27 MF |
350 | { |
351 | return float32_scalbn(uint32_to_float32(v, &env->fp_status), | |
352 | (int32_t)scale, &env->fp_status); | |
353 | } | |
354 | ||
cfa9f051 MF |
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 | ||
5dbb4c96 | 370 | uint32_t HELPER(un_s)(CPUXtensaState *env, float32 a, float32 b) |
9483bf27 | 371 | { |
5dbb4c96 | 372 | return float32_unordered_quiet(a, b, &env->fp_status); |
9483bf27 MF |
373 | } |
374 | ||
cfa9f051 MF |
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 | ||
5dbb4c96 | 380 | uint32_t HELPER(oeq_s)(CPUXtensaState *env, float32 a, float32 b) |
9483bf27 | 381 | { |
5dbb4c96 | 382 | return float32_eq_quiet(a, b, &env->fp_status); |
9483bf27 MF |
383 | } |
384 | ||
cfa9f051 MF |
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 | ||
5dbb4c96 | 393 | uint32_t HELPER(ueq_s)(CPUXtensaState *env, float32 a, float32 b) |
9483bf27 | 394 | { |
71bfd65c | 395 | FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); |
5dbb4c96 MF |
396 | |
397 | return v == float_relation_equal || | |
398 | v == float_relation_unordered; | |
9483bf27 MF |
399 | } |
400 | ||
cfa9f051 MF |
401 | uint32_t HELPER(olt_d)(CPUXtensaState *env, float64 a, float64 b) |
402 | { | |
403 | return float64_lt(a, b, &env->fp_status); | |
404 | } | |
405 | ||
5dbb4c96 | 406 | uint32_t HELPER(olt_s)(CPUXtensaState *env, float32 a, float32 b) |
9483bf27 | 407 | { |
cfa9f051 MF |
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; | |
9483bf27 MF |
417 | } |
418 | ||
5dbb4c96 | 419 | uint32_t HELPER(ult_s)(CPUXtensaState *env, float32 a, float32 b) |
9483bf27 | 420 | { |
71bfd65c | 421 | FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); |
5dbb4c96 MF |
422 | |
423 | return v == float_relation_less || | |
424 | v == float_relation_unordered; | |
9483bf27 MF |
425 | } |
426 | ||
cfa9f051 MF |
427 | uint32_t HELPER(ole_d)(CPUXtensaState *env, float64 a, float64 b) |
428 | { | |
429 | return float64_le(a, b, &env->fp_status); | |
430 | } | |
431 | ||
5dbb4c96 | 432 | uint32_t HELPER(ole_s)(CPUXtensaState *env, float32 a, float32 b) |
9483bf27 | 433 | { |
cfa9f051 MF |
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; | |
9483bf27 MF |
442 | } |
443 | ||
5dbb4c96 | 444 | uint32_t HELPER(ule_s)(CPUXtensaState *env, float32 a, float32 b) |
9483bf27 | 445 | { |
71bfd65c | 446 | FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); |
5dbb4c96 MF |
447 | |
448 | return v != float_relation_greater; | |
9483bf27 | 449 | } |