xref: /openbmc/qemu/target/xtensa/fpu_helper.c (revision f15f7273ea55472d5904c53566c82369d81214c1)
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