]> git.proxmox.com Git - mirror_qemu.git/blob - target/xtensa/fpu_helper.c
target/xtensa: add DFPU registers and opcodes
[mirror_qemu.git] / target / xtensa / fpu_helper.c
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/main-loop.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 uint32_t HELPER(ftoi_d)(CPUXtensaState *env, float64 v,
235 uint32_t rounding_mode, uint32_t scale)
236 {
237 float_status fp_status = env->fp_status;
238 uint32_t res;
239
240 set_float_rounding_mode(rounding_mode, &fp_status);
241 res = float64_to_int32(float64_scalbn(v, scale, &fp_status), &fp_status);
242 set_float_exception_flags(get_float_exception_flags(&fp_status),
243 &env->fp_status);
244 return res;
245 }
246
247 uint32_t HELPER(ftoi_s)(CPUXtensaState *env, float32 v,
248 uint32_t rounding_mode, uint32_t scale)
249 {
250 float_status fp_status = env->fp_status;
251 uint32_t res;
252
253 set_float_rounding_mode(rounding_mode, &fp_status);
254 res = float32_to_int32(float32_scalbn(v, scale, &fp_status), &fp_status);
255 set_float_exception_flags(get_float_exception_flags(&fp_status),
256 &env->fp_status);
257 return res;
258 }
259
260 uint32_t HELPER(ftoui_d)(CPUXtensaState *env, float64 v,
261 uint32_t rounding_mode, uint32_t scale)
262 {
263 float_status fp_status = env->fp_status;
264 float64 res;
265 uint32_t rv;
266
267 set_float_rounding_mode(rounding_mode, &fp_status);
268
269 res = float64_scalbn(v, scale, &fp_status);
270
271 if (float64_is_neg(v) && !float64_is_any_nan(v)) {
272 set_float_exception_flags(float_flag_invalid, &fp_status);
273 rv = float64_to_int32(res, &fp_status);
274 } else {
275 rv = float64_to_uint32(res, &fp_status);
276 }
277 set_float_exception_flags(get_float_exception_flags(&fp_status),
278 &env->fp_status);
279 return rv;
280 }
281
282 uint32_t HELPER(ftoui_s)(CPUXtensaState *env, float32 v,
283 uint32_t rounding_mode, uint32_t scale)
284 {
285 float_status fp_status = env->fp_status;
286 float32 res;
287 uint32_t rv;
288
289 set_float_rounding_mode(rounding_mode, &fp_status);
290
291 res = float32_scalbn(v, scale, &fp_status);
292
293 if (float32_is_neg(v) && !float32_is_any_nan(v)) {
294 rv = float32_to_int32(res, &fp_status);
295 if (rv) {
296 set_float_exception_flags(float_flag_invalid, &fp_status);
297 }
298 } else {
299 rv = float32_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 float64 HELPER(itof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale)
307 {
308 return float64_scalbn(int32_to_float64(v, &env->fp_status),
309 (int32_t)scale, &env->fp_status);
310 }
311
312 float32 HELPER(itof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale)
313 {
314 return float32_scalbn(int32_to_float32(v, &env->fp_status),
315 (int32_t)scale, &env->fp_status);
316 }
317
318 float64 HELPER(uitof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale)
319 {
320 return float64_scalbn(uint32_to_float64(v, &env->fp_status),
321 (int32_t)scale, &env->fp_status);
322 }
323
324 float32 HELPER(uitof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale)
325 {
326 return float32_scalbn(uint32_to_float32(v, &env->fp_status),
327 (int32_t)scale, &env->fp_status);
328 }
329
330 float64 HELPER(cvtd_s)(CPUXtensaState *env, float32 v)
331 {
332 return float32_to_float64(v, &env->fp_status);
333 }
334
335 float32 HELPER(cvts_d)(CPUXtensaState *env, float64 v)
336 {
337 return float64_to_float32(v, &env->fp_status);
338 }
339
340 uint32_t HELPER(un_d)(CPUXtensaState *env, float64 a, float64 b)
341 {
342 return float64_unordered_quiet(a, b, &env->fp_status);
343 }
344
345 uint32_t HELPER(un_s)(CPUXtensaState *env, float32 a, float32 b)
346 {
347 return float32_unordered_quiet(a, b, &env->fp_status);
348 }
349
350 uint32_t HELPER(oeq_d)(CPUXtensaState *env, float64 a, float64 b)
351 {
352 return float64_eq_quiet(a, b, &env->fp_status);
353 }
354
355 uint32_t HELPER(oeq_s)(CPUXtensaState *env, float32 a, float32 b)
356 {
357 return float32_eq_quiet(a, b, &env->fp_status);
358 }
359
360 uint32_t HELPER(ueq_d)(CPUXtensaState *env, float64 a, float64 b)
361 {
362 FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
363
364 return v == float_relation_equal ||
365 v == float_relation_unordered;
366 }
367
368 uint32_t HELPER(ueq_s)(CPUXtensaState *env, float32 a, float32 b)
369 {
370 FloatRelation v = float32_compare_quiet(a, b, &env->fp_status);
371
372 return v == float_relation_equal ||
373 v == float_relation_unordered;
374 }
375
376 uint32_t HELPER(olt_d)(CPUXtensaState *env, float64 a, float64 b)
377 {
378 return float64_lt(a, b, &env->fp_status);
379 }
380
381 uint32_t HELPER(olt_s)(CPUXtensaState *env, float32 a, float32 b)
382 {
383 return float32_lt(a, b, &env->fp_status);
384 }
385
386 uint32_t HELPER(ult_d)(CPUXtensaState *env, float64 a, float64 b)
387 {
388 FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
389
390 return v == float_relation_less ||
391 v == float_relation_unordered;
392 }
393
394 uint32_t HELPER(ult_s)(CPUXtensaState *env, float32 a, float32 b)
395 {
396 FloatRelation v = float32_compare_quiet(a, b, &env->fp_status);
397
398 return v == float_relation_less ||
399 v == float_relation_unordered;
400 }
401
402 uint32_t HELPER(ole_d)(CPUXtensaState *env, float64 a, float64 b)
403 {
404 return float64_le(a, b, &env->fp_status);
405 }
406
407 uint32_t HELPER(ole_s)(CPUXtensaState *env, float32 a, float32 b)
408 {
409 return float32_le(a, b, &env->fp_status);
410 }
411
412 uint32_t HELPER(ule_d)(CPUXtensaState *env, float64 a, float64 b)
413 {
414 FloatRelation v = float64_compare_quiet(a, b, &env->fp_status);
415
416 return v != float_relation_greater;
417 }
418
419 uint32_t HELPER(ule_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_greater;
424 }