| /* |
| * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * * Neither the name of the Open Source and Linux Lab nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/log.h" |
| #include "cpu.h" |
| #include "exec/helper-proto.h" |
| #include "qemu/host-utils.h" |
| #include "exec/exec-all.h" |
| #include "fpu/softfloat.h" |
| |
| enum { |
| XTENSA_FP_I = 0x1, |
| XTENSA_FP_U = 0x2, |
| XTENSA_FP_O = 0x4, |
| XTENSA_FP_Z = 0x8, |
| XTENSA_FP_V = 0x10, |
| }; |
| |
| enum { |
| XTENSA_FCR_FLAGS_SHIFT = 2, |
| XTENSA_FSR_FLAGS_SHIFT = 7, |
| }; |
| |
| static const struct { |
| uint32_t xtensa_fp_flag; |
| int softfloat_fp_flag; |
| } xtensa_fp_flag_map[] = { |
| { XTENSA_FP_I, float_flag_inexact, }, |
| { XTENSA_FP_U, float_flag_underflow, }, |
| { XTENSA_FP_O, float_flag_overflow, }, |
| { XTENSA_FP_Z, float_flag_divbyzero, }, |
| { XTENSA_FP_V, float_flag_invalid, }, |
| }; |
| |
| void xtensa_use_first_nan(CPUXtensaState *env, bool use_first) |
| { |
| set_use_first_nan(use_first, &env->fp_status); |
| set_float_2nan_prop_rule(use_first ? float_2nan_prop_ab : float_2nan_prop_ba, |
| &env->fp_status); |
| } |
| |
| void HELPER(wur_fpu2k_fcr)(CPUXtensaState *env, uint32_t v) |
| { |
| static const int rounding_mode[] = { |
| float_round_nearest_even, |
| float_round_to_zero, |
| float_round_up, |
| float_round_down, |
| }; |
| |
| env->uregs[FCR] = v & 0xfffff07f; |
| set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status); |
| } |
| |
| void HELPER(wur_fpu_fcr)(CPUXtensaState *env, uint32_t v) |
| { |
| static const int rounding_mode[] = { |
| float_round_nearest_even, |
| float_round_to_zero, |
| float_round_up, |
| float_round_down, |
| }; |
| |
| if (v & 0xfffff000) { |
| qemu_log_mask(LOG_GUEST_ERROR, |
| "MBZ field of FCR is written non-zero: %08x\n", v); |
| } |
| env->uregs[FCR] = v & 0x0000007f; |
| set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status); |
| } |
| |
| void HELPER(wur_fpu_fsr)(CPUXtensaState *env, uint32_t v) |
| { |
| uint32_t flags = v >> XTENSA_FSR_FLAGS_SHIFT; |
| int fef = 0; |
| unsigned i; |
| |
| if (v & 0xfffff000) { |
| qemu_log_mask(LOG_GUEST_ERROR, |
| "MBZ field of FSR is written non-zero: %08x\n", v); |
| } |
| env->uregs[FSR] = v & 0x00000f80; |
| for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) { |
| if (flags & xtensa_fp_flag_map[i].xtensa_fp_flag) { |
| fef |= xtensa_fp_flag_map[i].softfloat_fp_flag; |
| } |
| } |
| set_float_exception_flags(fef, &env->fp_status); |
| } |
| |
| uint32_t HELPER(rur_fpu_fsr)(CPUXtensaState *env) |
| { |
| uint32_t flags = 0; |
| int fef = get_float_exception_flags(&env->fp_status); |
| unsigned i; |
| |
| for (i = 0; i < ARRAY_SIZE(xtensa_fp_flag_map); ++i) { |
| if (fef & xtensa_fp_flag_map[i].softfloat_fp_flag) { |
| flags |= xtensa_fp_flag_map[i].xtensa_fp_flag; |
| } |
| } |
| env->uregs[FSR] = flags << XTENSA_FSR_FLAGS_SHIFT; |
| return flags << XTENSA_FSR_FLAGS_SHIFT; |
| } |
| |
| float64 HELPER(abs_d)(float64 v) |
| { |
| return float64_abs(v); |
| } |
| |
| float32 HELPER(abs_s)(float32 v) |
| { |
| return float32_abs(v); |
| } |
| |
| float64 HELPER(neg_d)(float64 v) |
| { |
| return float64_chs(v); |
| } |
| |
| float32 HELPER(neg_s)(float32 v) |
| { |
| return float32_chs(v); |
| } |
| |
| float32 HELPER(fpu2k_add_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| return float32_add(a, b, &env->fp_status); |
| } |
| |
| float32 HELPER(fpu2k_sub_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| return float32_sub(a, b, &env->fp_status); |
| } |
| |
| float32 HELPER(fpu2k_mul_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| return float32_mul(a, b, &env->fp_status); |
| } |
| |
| float32 HELPER(fpu2k_madd_s)(CPUXtensaState *env, |
| float32 a, float32 b, float32 c) |
| { |
| return float32_muladd(b, c, a, 0, &env->fp_status); |
| } |
| |
| float32 HELPER(fpu2k_msub_s)(CPUXtensaState *env, |
| float32 a, float32 b, float32 c) |
| { |
| return float32_muladd(b, c, a, float_muladd_negate_product, |
| &env->fp_status); |
| } |
| |
| float64 HELPER(add_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| xtensa_use_first_nan(env, true); |
| return float64_add(a, b, &env->fp_status); |
| } |
| |
| float32 HELPER(add_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| xtensa_use_first_nan(env, env->config->use_first_nan); |
| return float32_add(a, b, &env->fp_status); |
| } |
| |
| float64 HELPER(sub_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| xtensa_use_first_nan(env, true); |
| return float64_sub(a, b, &env->fp_status); |
| } |
| |
| float32 HELPER(sub_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| xtensa_use_first_nan(env, env->config->use_first_nan); |
| return float32_sub(a, b, &env->fp_status); |
| } |
| |
| float64 HELPER(mul_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| xtensa_use_first_nan(env, true); |
| return float64_mul(a, b, &env->fp_status); |
| } |
| |
| float32 HELPER(mul_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| xtensa_use_first_nan(env, env->config->use_first_nan); |
| return float32_mul(a, b, &env->fp_status); |
| } |
| |
| float64 HELPER(madd_d)(CPUXtensaState *env, float64 a, float64 b, float64 c) |
| { |
| xtensa_use_first_nan(env, env->config->use_first_nan); |
| return float64_muladd(b, c, a, 0, &env->fp_status); |
| } |
| |
| float32 HELPER(madd_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) |
| { |
| xtensa_use_first_nan(env, env->config->use_first_nan); |
| return float32_muladd(b, c, a, 0, &env->fp_status); |
| } |
| |
| float64 HELPER(msub_d)(CPUXtensaState *env, float64 a, float64 b, float64 c) |
| { |
| xtensa_use_first_nan(env, env->config->use_first_nan); |
| return float64_muladd(b, c, a, float_muladd_negate_product, |
| &env->fp_status); |
| } |
| |
| float32 HELPER(msub_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) |
| { |
| xtensa_use_first_nan(env, env->config->use_first_nan); |
| return float32_muladd(b, c, a, float_muladd_negate_product, |
| &env->fp_status); |
| } |
| |
| float64 HELPER(mkdadj_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| xtensa_use_first_nan(env, true); |
| return float64_div(b, a, &env->fp_status); |
| } |
| |
| float32 HELPER(mkdadj_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| xtensa_use_first_nan(env, env->config->use_first_nan); |
| return float32_div(b, a, &env->fp_status); |
| } |
| |
| float64 HELPER(mksadj_d)(CPUXtensaState *env, float64 v) |
| { |
| xtensa_use_first_nan(env, true); |
| return float64_sqrt(v, &env->fp_status); |
| } |
| |
| float32 HELPER(mksadj_s)(CPUXtensaState *env, float32 v) |
| { |
| xtensa_use_first_nan(env, env->config->use_first_nan); |
| return float32_sqrt(v, &env->fp_status); |
| } |
| |
| uint32_t HELPER(ftoi_d)(CPUXtensaState *env, float64 v, |
| uint32_t rounding_mode, uint32_t scale) |
| { |
| float_status fp_status = env->fp_status; |
| uint32_t res; |
| |
| set_float_rounding_mode(rounding_mode, &fp_status); |
| res = float64_to_int32(float64_scalbn(v, scale, &fp_status), &fp_status); |
| set_float_exception_flags(get_float_exception_flags(&fp_status), |
| &env->fp_status); |
| return res; |
| } |
| |
| uint32_t HELPER(ftoi_s)(CPUXtensaState *env, float32 v, |
| uint32_t rounding_mode, uint32_t scale) |
| { |
| float_status fp_status = env->fp_status; |
| uint32_t res; |
| |
| set_float_rounding_mode(rounding_mode, &fp_status); |
| res = float32_to_int32(float32_scalbn(v, scale, &fp_status), &fp_status); |
| set_float_exception_flags(get_float_exception_flags(&fp_status), |
| &env->fp_status); |
| return res; |
| } |
| |
| uint32_t HELPER(ftoui_d)(CPUXtensaState *env, float64 v, |
| uint32_t rounding_mode, uint32_t scale) |
| { |
| float_status fp_status = env->fp_status; |
| float64 res; |
| uint32_t rv; |
| |
| set_float_rounding_mode(rounding_mode, &fp_status); |
| |
| res = float64_scalbn(v, scale, &fp_status); |
| |
| if (float64_is_neg(v) && !float64_is_any_nan(v)) { |
| set_float_exception_flags(float_flag_invalid, &fp_status); |
| rv = float64_to_int32(res, &fp_status); |
| } else { |
| rv = float64_to_uint32(res, &fp_status); |
| } |
| set_float_exception_flags(get_float_exception_flags(&fp_status), |
| &env->fp_status); |
| return rv; |
| } |
| |
| uint32_t HELPER(ftoui_s)(CPUXtensaState *env, float32 v, |
| uint32_t rounding_mode, uint32_t scale) |
| { |
| float_status fp_status = env->fp_status; |
| float32 res; |
| uint32_t rv; |
| |
| set_float_rounding_mode(rounding_mode, &fp_status); |
| |
| res = float32_scalbn(v, scale, &fp_status); |
| |
| if (float32_is_neg(v) && !float32_is_any_nan(v)) { |
| rv = float32_to_int32(res, &fp_status); |
| if (rv) { |
| set_float_exception_flags(float_flag_invalid, &fp_status); |
| } |
| } else { |
| rv = float32_to_uint32(res, &fp_status); |
| } |
| set_float_exception_flags(get_float_exception_flags(&fp_status), |
| &env->fp_status); |
| return rv; |
| } |
| |
| float64 HELPER(itof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale) |
| { |
| return float64_scalbn(int32_to_float64(v, &env->fp_status), |
| (int32_t)scale, &env->fp_status); |
| } |
| |
| float32 HELPER(itof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale) |
| { |
| return float32_scalbn(int32_to_float32(v, &env->fp_status), |
| (int32_t)scale, &env->fp_status); |
| } |
| |
| float64 HELPER(uitof_d)(CPUXtensaState *env, uint32_t v, uint32_t scale) |
| { |
| return float64_scalbn(uint32_to_float64(v, &env->fp_status), |
| (int32_t)scale, &env->fp_status); |
| } |
| |
| float32 HELPER(uitof_s)(CPUXtensaState *env, uint32_t v, uint32_t scale) |
| { |
| return float32_scalbn(uint32_to_float32(v, &env->fp_status), |
| (int32_t)scale, &env->fp_status); |
| } |
| |
| float64 HELPER(cvtd_s)(CPUXtensaState *env, float32 v) |
| { |
| return float32_to_float64(v, &env->fp_status); |
| } |
| |
| float32 HELPER(cvts_d)(CPUXtensaState *env, float64 v) |
| { |
| return float64_to_float32(v, &env->fp_status); |
| } |
| |
| uint32_t HELPER(un_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| return float64_unordered_quiet(a, b, &env->fp_status); |
| } |
| |
| uint32_t HELPER(un_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| return float32_unordered_quiet(a, b, &env->fp_status); |
| } |
| |
| uint32_t HELPER(oeq_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| return float64_eq_quiet(a, b, &env->fp_status); |
| } |
| |
| uint32_t HELPER(oeq_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| return float32_eq_quiet(a, b, &env->fp_status); |
| } |
| |
| uint32_t HELPER(ueq_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| FloatRelation v = float64_compare_quiet(a, b, &env->fp_status); |
| |
| return v == float_relation_equal || |
| v == float_relation_unordered; |
| } |
| |
| uint32_t HELPER(ueq_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); |
| |
| return v == float_relation_equal || |
| v == float_relation_unordered; |
| } |
| |
| uint32_t HELPER(olt_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| return float64_lt(a, b, &env->fp_status); |
| } |
| |
| uint32_t HELPER(olt_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| return float32_lt(a, b, &env->fp_status); |
| } |
| |
| uint32_t HELPER(ult_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| FloatRelation v = float64_compare_quiet(a, b, &env->fp_status); |
| |
| return v == float_relation_less || |
| v == float_relation_unordered; |
| } |
| |
| uint32_t HELPER(ult_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); |
| |
| return v == float_relation_less || |
| v == float_relation_unordered; |
| } |
| |
| uint32_t HELPER(ole_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| return float64_le(a, b, &env->fp_status); |
| } |
| |
| uint32_t HELPER(ole_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| return float32_le(a, b, &env->fp_status); |
| } |
| |
| uint32_t HELPER(ule_d)(CPUXtensaState *env, float64 a, float64 b) |
| { |
| FloatRelation v = float64_compare_quiet(a, b, &env->fp_status); |
| |
| return v != float_relation_greater; |
| } |
| |
| uint32_t HELPER(ule_s)(CPUXtensaState *env, float32 a, float32 b) |
| { |
| FloatRelation v = float32_compare_quiet(a, b, &env->fp_status); |
| |
| return v != float_relation_greater; |
| } |