/* * PowerPC floating point and SPE emulation helpers for QEMU. * * Copyright (c) 2003-2007 Jocelyn Mayer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ #include "cpu.h" #include "helper.h" /*****************************************************************************/ /* Floating point operations helpers */ uint64_t helper_float32_to_float64(CPUPPCState *env, uint32_t arg) { CPU_FloatU f; CPU_DoubleU d; f.l = arg; d.d = float32_to_float64(f.f, &env->fp_status); return d.ll; } uint32_t helper_float64_to_float32(CPUPPCState *env, uint64_t arg) { CPU_FloatU f; CPU_DoubleU d; d.ll = arg; f.f = float64_to_float32(d.d, &env->fp_status); return f.l; } static inline int isden(float64 d) { CPU_DoubleU u; u.d = d; return ((u.ll >> 52) & 0x7FF) == 0; } uint32_t helper_compute_fprf(CPUPPCState *env, uint64_t arg, uint32_t set_fprf) { CPU_DoubleU farg; int isneg; int ret; farg.ll = arg; isneg = float64_is_neg(farg.d); if (unlikely(float64_is_any_nan(farg.d))) { if (float64_is_signaling_nan(farg.d)) { /* Signaling NaN: flags are undefined */ ret = 0x00; } else { /* Quiet NaN */ ret = 0x11; } } else if (unlikely(float64_is_infinity(farg.d))) { /* +/- infinity */ if (isneg) { ret = 0x09; } else { ret = 0x05; } } else { if (float64_is_zero(farg.d)) { /* +/- zero */ if (isneg) { ret = 0x12; } else { ret = 0x02; } } else { if (isden(farg.d)) { /* Denormalized numbers */ ret = 0x10; } else { /* Normalized numbers */ ret = 0x00; } if (isneg) { ret |= 0x08; } else { ret |= 0x04; } } } if (set_fprf) { /* We update FPSCR_FPRF */ env->fpscr &= ~(0x1F << FPSCR_FPRF); env->fpscr |= ret << FPSCR_FPRF; } /* We just need fpcc to update Rc1 */ return ret & 0xF; } /* Floating-point invalid operations exception */ static inline uint64_t fload_invalid_op_excp(CPUPPCState *env, int op) { uint64_t ret = 0; int ve; ve = fpscr_ve; switch (op) { case POWERPC_EXCP_FP_VXSNAN: env->fpscr |= 1 << FPSCR_VXSNAN; break; case POWERPC_EXCP_FP_VXSOFT: env->fpscr |= 1 << FPSCR_VXSOFT; break; case POWERPC_EXCP_FP_VXISI: /* Magnitude subtraction of infinities */ env->fpscr |= 1 << FPSCR_VXISI; goto update_arith; case POWERPC_EXCP_FP_VXIDI: /* Division of infinity by infinity */ env->fpscr |= 1 << FPSCR_VXIDI; goto update_arith; case POWERPC_EXCP_FP_VXZDZ: /* Division of zero by zero */ env->fpscr |= 1 << FPSCR_VXZDZ; goto update_arith; case POWERPC_EXCP_FP_VXIMZ: /* Multiplication of zero by infinity */ env->fpscr |= 1 << FPSCR_VXIMZ; goto update_arith; case POWERPC_EXCP_FP_VXVC: /* Ordered comparison of NaN */ env->fpscr |= 1 << FPSCR_VXVC; env->fpscr &= ~(0xF << FPSCR_FPCC); env->fpscr |= 0x11 << FPSCR_FPCC; /* We must update the target FPR before raising the exception */ if (ve != 0) { env->exception_index = POWERPC_EXCP_PROGRAM; env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_VXVC; /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; /* Exception is differed */ ve = 0; } break; case POWERPC_EXCP_FP_VXSQRT: /* Square root of a negative number */ env->fpscr |= 1 << FPSCR_VXSQRT; update_arith: env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); if (ve == 0) { /* Set the result to quiet NaN */ ret = 0x7FF8000000000000ULL; env->fpscr &= ~(0xF << FPSCR_FPCC); env->fpscr |= 0x11 << FPSCR_FPCC; } break; case POWERPC_EXCP_FP_VXCVI: /* Invalid conversion */ env->fpscr |= 1 << FPSCR_VXCVI; env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); if (ve == 0) { /* Set the result to quiet NaN */ ret = 0x7FF8000000000000ULL; env->fpscr &= ~(0xF << FPSCR_FPCC); env->fpscr |= 0x11 << FPSCR_FPCC; } break; } /* Update the floating-point invalid operation summary */ env->fpscr |= 1 << FPSCR_VX; /* Update the floating-point exception summary */ env->fpscr |= 1 << FPSCR_FX; if (ve != 0) { /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; if (msr_fe0 != 0 || msr_fe1 != 0) { helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_FP | op); } } return ret; } static inline void float_zero_divide_excp(CPUPPCState *env) { env->fpscr |= 1 << FPSCR_ZX; env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); /* Update the floating-point exception summary */ env->fpscr |= 1 << FPSCR_FX; if (fpscr_ze != 0) { /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; if (msr_fe0 != 0 || msr_fe1 != 0) { helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX); } } } static inline void float_overflow_excp(CPUPPCState *env) { env->fpscr |= 1 << FPSCR_OX; /* Update the floating-point exception summary */ env->fpscr |= 1 << FPSCR_FX; if (fpscr_oe != 0) { /* XXX: should adjust the result */ /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; /* We must update the target FPR before raising the exception */ env->exception_index = POWERPC_EXCP_PROGRAM; env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX; } else { env->fpscr |= 1 << FPSCR_XX; env->fpscr |= 1 << FPSCR_FI; } } static inline void float_underflow_excp(CPUPPCState *env) { env->fpscr |= 1 << FPSCR_UX; /* Update the floating-point exception summary */ env->fpscr |= 1 << FPSCR_FX; if (fpscr_ue != 0) { /* XXX: should adjust the result */ /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; /* We must update the target FPR before raising the exception */ env->exception_index = POWERPC_EXCP_PROGRAM; env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX; } } static inline void float_inexact_excp(CPUPPCState *env) { env->fpscr |= 1 << FPSCR_XX; /* Update the floating-point exception summary */ env->fpscr |= 1 << FPSCR_FX; if (fpscr_xe != 0) { /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; /* We must update the target FPR before raising the exception */ env->exception_index = POWERPC_EXCP_PROGRAM; env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX; } } static inline void fpscr_set_rounding_mode(CPUPPCState *env) { int rnd_type; /* Set rounding mode */ switch (fpscr_rn) { case 0: /* Best approximation (round to nearest) */ rnd_type = float_round_nearest_even; break; case 1: /* Smaller magnitude (round toward zero) */ rnd_type = float_round_to_zero; break; case 2: /* Round toward +infinite */ rnd_type = float_round_up; break; default: case 3: /* Round toward -infinite */ rnd_type = float_round_down; break; } set_float_rounding_mode(rnd_type, &env->fp_status); } void helper_fpscr_clrbit(CPUPPCState *env, uint32_t bit) { int prev; prev = (env->fpscr >> bit) & 1; env->fpscr &= ~(1 << bit); if (prev == 1) { switch (bit) { case FPSCR_RN1: case FPSCR_RN: fpscr_set_rounding_mode(env); break; default: break; } } } void helper_fpscr_setbit(CPUPPCState *env, uint32_t bit) { int prev; prev = (env->fpscr >> bit) & 1; env->fpscr |= 1 << bit; if (prev == 0) { switch (bit) { case FPSCR_VX: env->fpscr |= 1 << FPSCR_FX; if (fpscr_ve) { goto raise_ve; } break; case FPSCR_OX: env->fpscr |= 1 << FPSCR_FX; if (fpscr_oe) { goto raise_oe; } break; case FPSCR_UX: env->fpscr |= 1 << FPSCR_FX; if (fpscr_ue) { goto raise_ue; } break; case FPSCR_ZX: env->fpscr |= 1 << FPSCR_FX; if (fpscr_ze) { goto raise_ze; } break; case FPSCR_XX: env->fpscr |= 1 << FPSCR_FX; if (fpscr_xe) { goto raise_xe; } break; case FPSCR_VXSNAN: case FPSCR_VXISI: case FPSCR_VXIDI: case FPSCR_VXZDZ: case FPSCR_VXIMZ: case FPSCR_VXVC: case FPSCR_VXSOFT: case FPSCR_VXSQRT: case FPSCR_VXCVI: env->fpscr |= 1 << FPSCR_VX; env->fpscr |= 1 << FPSCR_FX; if (fpscr_ve != 0) { goto raise_ve; } break; case FPSCR_VE: if (fpscr_vx != 0) { raise_ve: env->error_code = POWERPC_EXCP_FP; if (fpscr_vxsnan) { env->error_code |= POWERPC_EXCP_FP_VXSNAN; } if (fpscr_vxisi) { env->error_code |= POWERPC_EXCP_FP_VXISI; } if (fpscr_vxidi) { env->error_code |= POWERPC_EXCP_FP_VXIDI; } if (fpscr_vxzdz) { env->error_code |= POWERPC_EXCP_FP_VXZDZ; } if (fpscr_vximz) { env->error_code |= POWERPC_EXCP_FP_VXIMZ; } if (fpscr_vxvc) { env->error_code |= POWERPC_EXCP_FP_VXVC; } if (fpscr_vxsoft) { env->error_code |= POWERPC_EXCP_FP_VXSOFT; } if (fpscr_vxsqrt) { env->error_code |= POWERPC_EXCP_FP_VXSQRT; } if (fpscr_vxcvi) { env->error_code |= POWERPC_EXCP_FP_VXCVI; } goto raise_excp; } break; case FPSCR_OE: if (fpscr_ox != 0) { raise_oe: env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX; goto raise_excp; } break; case FPSCR_UE: if (fpscr_ux != 0) { raise_ue: env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX; goto raise_excp; } break; case FPSCR_ZE: if (fpscr_zx != 0) { raise_ze: env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX; goto raise_excp; } break; case FPSCR_XE: if (fpscr_xx != 0) { raise_xe: env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX; goto raise_excp; } break; case FPSCR_RN1: case FPSCR_RN: fpscr_set_rounding_mode(env); break; default: break; raise_excp: /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; /* We have to update Rc1 before raising the exception */ env->exception_index = POWERPC_EXCP_PROGRAM; break; } } } void helper_store_fpscr(CPUPPCState *env, uint64_t arg, uint32_t mask) { /* * We use only the 32 LSB of the incoming fpr */ uint32_t prev, new; int i; prev = env->fpscr; new = (uint32_t)arg; new &= ~0x60000000; new |= prev & 0x60000000; for (i = 0; i < 8; i++) { if (mask & (1 << i)) { env->fpscr &= ~(0xF << (4 * i)); env->fpscr |= new & (0xF << (4 * i)); } } /* Update VX and FEX */ if (fpscr_ix != 0) { env->fpscr |= 1 << FPSCR_VX; } else { env->fpscr &= ~(1 << FPSCR_VX); } if ((fpscr_ex & fpscr_eex) != 0) { env->fpscr |= 1 << FPSCR_FEX; env->exception_index = POWERPC_EXCP_PROGRAM; /* XXX: we should compute it properly */ env->error_code = POWERPC_EXCP_FP; } else { env->fpscr &= ~(1 << FPSCR_FEX); } fpscr_set_rounding_mode(env); } void helper_float_check_status(CPUPPCState *env) { if (env->exception_index == POWERPC_EXCP_PROGRAM && (env->error_code & POWERPC_EXCP_FP)) { /* Differred floating-point exception after target FPR update */ if (msr_fe0 != 0 || msr_fe1 != 0) { helper_raise_exception_err(env, env->exception_index, env->error_code); } } else { int status = get_float_exception_flags(&env->fp_status); if (status & float_flag_divbyzero) { float_zero_divide_excp(env); } else if (status & float_flag_overflow) { float_overflow_excp(env); } else if (status & float_flag_underflow) { float_underflow_excp(env); } else if (status & float_flag_inexact) { float_inexact_excp(env); } } } void helper_reset_fpstatus(CPUPPCState *env) { set_float_exception_flags(0, &env->fp_status); } /* fadd - fadd. */ uint64_t helper_fadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2) { CPU_DoubleU farg1, farg2; farg1.ll = arg1; farg2.ll = arg2; if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { /* Magnitude subtraction of infinities */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN addition */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); } return farg1.ll; } /* fsub - fsub. */ uint64_t helper_fsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2) { CPU_DoubleU farg1, farg2; farg1.ll = arg1; farg2.ll = arg2; if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { /* Magnitude subtraction of infinities */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN subtraction */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); } return farg1.ll; } /* fmul - fmul. */ uint64_t helper_fmul(CPUPPCState *env, uint64_t arg1, uint64_t arg2) { CPU_DoubleU farg1, farg2; farg1.ll = arg1; farg2.ll = arg2; if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN multiplication */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); } return farg1.ll; } /* fdiv - fdiv. */ uint64_t helper_fdiv(CPUPPCState *env, uint64_t arg1, uint64_t arg2) { CPU_DoubleU farg1, farg2; farg1.ll = arg1; farg2.ll = arg2; if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d))) { /* Division of infinity by infinity */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI); } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) { /* Division of zero by zero */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d))) { /* sNaN division */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); } return farg1.ll; } /* fabs */ uint64_t helper_fabs(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; farg.ll = arg; farg.d = float64_abs(farg.d); return farg.ll; } /* fnabs */ uint64_t helper_fnabs(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; farg.ll = arg; farg.d = float64_abs(farg.d); farg.d = float64_chs(farg.d); return farg.ll; } /* fneg */ uint64_t helper_fneg(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; farg.ll = arg; farg.d = float64_chs(farg.d); return farg.ll; } /* fctiw - fctiw. */ uint64_t helper_fctiw(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; farg.ll = arg; if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN conversion */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { /* qNan / infinity conversion */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); } else { farg.ll = float64_to_int32(farg.d, &env->fp_status); /* XXX: higher bits are not supposed to be significant. * to make tests easier, return the same as a real PowerPC 750 */ farg.ll |= 0xFFF80000ULL << 32; } return farg.ll; } /* fctiwz - fctiwz. */ uint64_t helper_fctiwz(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; farg.ll = arg; if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN conversion */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { /* qNan / infinity conversion */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); } else { farg.ll = float64_to_int32_round_to_zero(farg.d, &env->fp_status); /* XXX: higher bits are not supposed to be significant. * to make tests easier, return the same as a real PowerPC 750 */ farg.ll |= 0xFFF80000ULL << 32; } return farg.ll; } #if defined(TARGET_PPC64) /* fcfid - fcfid. */ uint64_t helper_fcfid(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; farg.d = int64_to_float64(arg, &env->fp_status); return farg.ll; } /* fctid - fctid. */ uint64_t helper_fctid(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; farg.ll = arg; if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN conversion */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { /* qNan / infinity conversion */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); } else { farg.ll = float64_to_int64(farg.d, &env->fp_status); } return farg.ll; } /* fctidz - fctidz. */ uint64_t helper_fctidz(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; farg.ll = arg; if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN conversion */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { /* qNan / infinity conversion */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); } else { farg.ll = float64_to_int64_round_to_zero(farg.d, &env->fp_status); } return farg.ll; } #endif static inline uint64_t do_fri(CPUPPCState *env, uint64_t arg, int rounding_mode) { CPU_DoubleU farg; farg.ll = arg; if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN round */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { /* qNan / infinity round */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI); } else { set_float_rounding_mode(rounding_mode, &env->fp_status); farg.ll = float64_round_to_int(farg.d, &env->fp_status); /* Restore rounding mode from FPSCR */ fpscr_set_rounding_mode(env); } return farg.ll; } uint64_t helper_frin(CPUPPCState *env, uint64_t arg) { return do_fri(env, arg, float_round_nearest_even); } uint64_t helper_friz(CPUPPCState *env, uint64_t arg) { return do_fri(env, arg, float_round_to_zero); } uint64_t helper_frip(CPUPPCState *env, uint64_t arg) { return do_fri(env, arg, float_round_up); } uint64_t helper_frim(CPUPPCState *env, uint64_t arg) { return do_fri(env, arg, float_round_down); } /* fmadd - fmadd. */ uint64_t helper_fmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint64_t arg3) { CPU_DoubleU farg1, farg2, farg3; farg1.ll = arg1; farg2.ll = arg2; farg3.ll = arg3; if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; ft0_128 = float64_to_float128(farg1.d, &env->fp_status); ft1_128 = float64_to_float128(farg2.d, &env->fp_status); ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); farg1.d = float128_to_float64(ft0_128, &env->fp_status); } } return farg1.ll; } /* fmsub - fmsub. */ uint64_t helper_fmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint64_t arg3) { CPU_DoubleU farg1, farg2, farg3; farg1.ll = arg1; farg2.ll = arg2; farg3.ll = arg3; if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; ft0_128 = float64_to_float128(farg1.d, &env->fp_status); ft1_128 = float64_to_float128(farg2.d, &env->fp_status); ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); farg1.d = float128_to_float64(ft0_128, &env->fp_status); } } return farg1.ll; } /* fnmadd - fnmadd. */ uint64_t helper_fnmadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint64_t arg3) { CPU_DoubleU farg1, farg2, farg3; farg1.ll = arg1; farg2.ll = arg2; farg3.ll = arg3; if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; ft0_128 = float64_to_float128(farg1.d, &env->fp_status); ft1_128 = float64_to_float128(farg2.d, &env->fp_status); ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) != float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); farg1.d = float128_to_float64(ft0_128, &env->fp_status); } if (likely(!float64_is_any_nan(farg1.d))) { farg1.d = float64_chs(farg1.d); } } return farg1.ll; } /* fnmsub - fnmsub. */ uint64_t helper_fnmsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint64_t arg3) { CPU_DoubleU farg1, farg2, farg3; farg1.ll = arg1; farg2.ll = arg2; farg3.ll = arg3; if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ); } else { if (unlikely(float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d) || float64_is_signaling_nan(farg3.d))) { /* sNaN operation */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; ft0_128 = float64_to_float128(farg1.d, &env->fp_status); ft1_128 = float64_to_float128(farg2.d, &env->fp_status); ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); if (unlikely(float128_is_infinity(ft0_128) && float64_is_infinity(farg3.d) && float128_is_neg(ft0_128) == float64_is_neg(farg3.d))) { /* Magnitude subtraction of infinities */ farg1.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI); } else { ft1_128 = float64_to_float128(farg3.d, &env->fp_status); ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); farg1.d = float128_to_float64(ft0_128, &env->fp_status); } if (likely(!float64_is_any_nan(farg1.d))) { farg1.d = float64_chs(farg1.d); } } return farg1.ll; } /* frsp - frsp. */ uint64_t helper_frsp(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; float32 f32; farg.ll = arg; if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN square root */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } f32 = float64_to_float32(farg.d, &env->fp_status); farg.d = float32_to_float64(f32, &env->fp_status); return farg.ll; } /* fsqrt - fsqrt. */ uint64_t helper_fsqrt(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; farg.ll = arg; if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { /* Square root of a negative nonzero number */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT); } else { if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN square root */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } farg.d = float64_sqrt(farg.d, &env->fp_status); } return farg.ll; } /* fre - fre. */ uint64_t helper_fre(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; farg.ll = arg; if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } farg.d = float64_div(float64_one, farg.d, &env->fp_status); return farg.d; } /* fres - fres. */ uint64_t helper_fres(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; float32 f32; farg.ll = arg; if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } farg.d = float64_div(float64_one, farg.d, &env->fp_status); f32 = float64_to_float32(farg.d, &env->fp_status); farg.d = float32_to_float64(f32, &env->fp_status); return farg.ll; } /* frsqrte - frsqrte. */ uint64_t helper_frsqrte(CPUPPCState *env, uint64_t arg) { CPU_DoubleU farg; float32 f32; farg.ll = arg; if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { /* Reciprocal square root of a negative nonzero number */ farg.ll = fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT); } else { if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal square root */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } farg.d = float64_sqrt(farg.d, &env->fp_status); farg.d = float64_div(float64_one, farg.d, &env->fp_status); f32 = float64_to_float32(farg.d, &env->fp_status); farg.d = float32_to_float64(f32, &env->fp_status); } return farg.ll; } /* fsel - fsel. */ uint64_t helper_fsel(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint64_t arg3) { CPU_DoubleU farg1; farg1.ll = arg1; if ((!float64_is_neg(farg1.d) || float64_is_zero(farg1.d)) && !float64_is_any_nan(farg1.d)) { return arg2; } else { return arg3; } } void helper_fcmpu(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint32_t crfD) { CPU_DoubleU farg1, farg2; uint32_t ret = 0; farg1.ll = arg1; farg2.ll = arg2; if (unlikely(float64_is_any_nan(farg1.d) || float64_is_any_nan(farg2.d))) { ret = 0x01UL; } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { ret = 0x08UL; } else if (!float64_le(farg1.d, farg2.d, &env->fp_status)) { ret = 0x04UL; } else { ret = 0x02UL; } env->fpscr &= ~(0x0F << FPSCR_FPRF); env->fpscr |= ret << FPSCR_FPRF; env->crf[crfD] = ret; if (unlikely(ret == 0x01UL && (float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d)))) { /* sNaN comparison */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN); } } void helper_fcmpo(CPUPPCState *env, uint64_t arg1, uint64_t arg2, uint32_t crfD) { CPU_DoubleU farg1, farg2; uint32_t ret = 0; farg1.ll = arg1; farg2.ll = arg2; if (unlikely(float64_is_any_nan(farg1.d) || float64_is_any_nan(farg2.d))) { ret = 0x01UL; } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { ret = 0x08UL; } else if (!float64_le(farg1.d, farg2.d, &env->fp_status)) { ret = 0x04UL; } else { ret = 0x02UL; } env->fpscr &= ~(0x0F << FPSCR_FPRF); env->fpscr |= ret << FPSCR_FPRF; env->crf[crfD] = ret; if (unlikely(ret == 0x01UL)) { if (float64_is_signaling_nan(farg1.d) || float64_is_signaling_nan(farg2.d)) { /* sNaN comparison */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXVC); } else { /* qNaN comparison */ fload_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC); } } } /* Single-precision floating-point conversions */ static inline uint32_t efscfsi(CPUPPCState *env, uint32_t val) { CPU_FloatU u; u.f = int32_to_float32(val, &env->vec_status); return u.l; } static inline uint32_t efscfui(CPUPPCState *env, uint32_t val) { CPU_FloatU u; u.f = uint32_to_float32(val, &env->vec_status); return u.l; } static inline int32_t efsctsi(CPUPPCState *env, uint32_t val) { CPU_FloatU u; u.l = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float32_is_quiet_nan(u.f))) { return 0; } return float32_to_int32(u.f, &env->vec_status); } static inline uint32_t efsctui(CPUPPCState *env, uint32_t val) { CPU_FloatU u; u.l = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float32_is_quiet_nan(u.f))) { return 0; } return float32_to_uint32(u.f, &env->vec_status); } static inline uint32_t efsctsiz(CPUPPCState *env, uint32_t val) { CPU_FloatU u; u.l = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float32_is_quiet_nan(u.f))) { return 0; } return float32_to_int32_round_to_zero(u.f, &env->vec_status); } static inline uint32_t efsctuiz(CPUPPCState *env, uint32_t val) { CPU_FloatU u; u.l = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float32_is_quiet_nan(u.f))) { return 0; } return float32_to_uint32_round_to_zero(u.f, &env->vec_status); } static inline uint32_t efscfsf(CPUPPCState *env, uint32_t val) { CPU_FloatU u; float32 tmp; u.f = int32_to_float32(val, &env->vec_status); tmp = int64_to_float32(1ULL << 32, &env->vec_status); u.f = float32_div(u.f, tmp, &env->vec_status); return u.l; } static inline uint32_t efscfuf(CPUPPCState *env, uint32_t val) { CPU_FloatU u; float32 tmp; u.f = uint32_to_float32(val, &env->vec_status); tmp = uint64_to_float32(1ULL << 32, &env->vec_status); u.f = float32_div(u.f, tmp, &env->vec_status); return u.l; } static inline uint32_t efsctsf(CPUPPCState *env, uint32_t val) { CPU_FloatU u; float32 tmp; u.l = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float32_is_quiet_nan(u.f))) { return 0; } tmp = uint64_to_float32(1ULL << 32, &env->vec_status); u.f = float32_mul(u.f, tmp, &env->vec_status); return float32_to_int32(u.f, &env->vec_status); } static inline uint32_t efsctuf(CPUPPCState *env, uint32_t val) { CPU_FloatU u; float32 tmp; u.l = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float32_is_quiet_nan(u.f))) { return 0; } tmp = uint64_to_float32(1ULL << 32, &env->vec_status); u.f = float32_mul(u.f, tmp, &env->vec_status); return float32_to_uint32(u.f, &env->vec_status); } #define HELPER_SPE_SINGLE_CONV(name) \ uint32_t helper_e##name(CPUPPCState *env, uint32_t val) \ { \ return e##name(env, val); \ } /* efscfsi */ HELPER_SPE_SINGLE_CONV(fscfsi); /* efscfui */ HELPER_SPE_SINGLE_CONV(fscfui); /* efscfuf */ HELPER_SPE_SINGLE_CONV(fscfuf); /* efscfsf */ HELPER_SPE_SINGLE_CONV(fscfsf); /* efsctsi */ HELPER_SPE_SINGLE_CONV(fsctsi); /* efsctui */ HELPER_SPE_SINGLE_CONV(fsctui); /* efsctsiz */ HELPER_SPE_SINGLE_CONV(fsctsiz); /* efsctuiz */ HELPER_SPE_SINGLE_CONV(fsctuiz); /* efsctsf */ HELPER_SPE_SINGLE_CONV(fsctsf); /* efsctuf */ HELPER_SPE_SINGLE_CONV(fsctuf); #define HELPER_SPE_VECTOR_CONV(name) \ uint64_t helper_ev##name(CPUPPCState *env, uint64_t val) \ { \ return ((uint64_t)e##name(env, val >> 32) << 32) | \ (uint64_t)e##name(env, val); \ } /* evfscfsi */ HELPER_SPE_VECTOR_CONV(fscfsi); /* evfscfui */ HELPER_SPE_VECTOR_CONV(fscfui); /* evfscfuf */ HELPER_SPE_VECTOR_CONV(fscfuf); /* evfscfsf */ HELPER_SPE_VECTOR_CONV(fscfsf); /* evfsctsi */ HELPER_SPE_VECTOR_CONV(fsctsi); /* evfsctui */ HELPER_SPE_VECTOR_CONV(fsctui); /* evfsctsiz */ HELPER_SPE_VECTOR_CONV(fsctsiz); /* evfsctuiz */ HELPER_SPE_VECTOR_CONV(fsctuiz); /* evfsctsf */ HELPER_SPE_VECTOR_CONV(fsctsf); /* evfsctuf */ HELPER_SPE_VECTOR_CONV(fsctuf); /* Single-precision floating-point arithmetic */ static inline uint32_t efsadd(CPUPPCState *env, uint32_t op1, uint32_t op2) { CPU_FloatU u1, u2; u1.l = op1; u2.l = op2; u1.f = float32_add(u1.f, u2.f, &env->vec_status); return u1.l; } static inline uint32_t efssub(CPUPPCState *env, uint32_t op1, uint32_t op2) { CPU_FloatU u1, u2; u1.l = op1; u2.l = op2; u1.f = float32_sub(u1.f, u2.f, &env->vec_status); return u1.l; } static inline uint32_t efsmul(CPUPPCState *env, uint32_t op1, uint32_t op2) { CPU_FloatU u1, u2; u1.l = op1; u2.l = op2; u1.f = float32_mul(u1.f, u2.f, &env->vec_status); return u1.l; } static inline uint32_t efsdiv(CPUPPCState *env, uint32_t op1, uint32_t op2) { CPU_FloatU u1, u2; u1.l = op1; u2.l = op2; u1.f = float32_div(u1.f, u2.f, &env->vec_status); return u1.l; } #define HELPER_SPE_SINGLE_ARITH(name) \ uint32_t helper_e##name(CPUPPCState *env, uint32_t op1, uint32_t op2) \ { \ return e##name(env, op1, op2); \ } /* efsadd */ HELPER_SPE_SINGLE_ARITH(fsadd); /* efssub */ HELPER_SPE_SINGLE_ARITH(fssub); /* efsmul */ HELPER_SPE_SINGLE_ARITH(fsmul); /* efsdiv */ HELPER_SPE_SINGLE_ARITH(fsdiv); #define HELPER_SPE_VECTOR_ARITH(name) \ uint64_t helper_ev##name(CPUPPCState *env, uint64_t op1, uint64_t op2) \ { \ return ((uint64_t)e##name(env, op1 >> 32, op2 >> 32) << 32) | \ (uint64_t)e##name(env, op1, op2); \ } /* evfsadd */ HELPER_SPE_VECTOR_ARITH(fsadd); /* evfssub */ HELPER_SPE_VECTOR_ARITH(fssub); /* evfsmul */ HELPER_SPE_VECTOR_ARITH(fsmul); /* evfsdiv */ HELPER_SPE_VECTOR_ARITH(fsdiv); /* Single-precision floating-point comparisons */ static inline uint32_t efscmplt(CPUPPCState *env, uint32_t op1, uint32_t op2) { CPU_FloatU u1, u2; u1.l = op1; u2.l = op2; return float32_lt(u1.f, u2.f, &env->vec_status) ? 4 : 0; } static inline uint32_t efscmpgt(CPUPPCState *env, uint32_t op1, uint32_t op2) { CPU_FloatU u1, u2; u1.l = op1; u2.l = op2; return float32_le(u1.f, u2.f, &env->vec_status) ? 0 : 4; } static inline uint32_t efscmpeq(CPUPPCState *env, uint32_t op1, uint32_t op2) { CPU_FloatU u1, u2; u1.l = op1; u2.l = op2; return float32_eq(u1.f, u2.f, &env->vec_status) ? 4 : 0; } static inline uint32_t efststlt(CPUPPCState *env, uint32_t op1, uint32_t op2) { /* XXX: TODO: ignore special values (NaN, infinites, ...) */ return efscmplt(env, op1, op2); } static inline uint32_t efststgt(CPUPPCState *env, uint32_t op1, uint32_t op2) { /* XXX: TODO: ignore special values (NaN, infinites, ...) */ return efscmpgt(env, op1, op2); } static inline uint32_t efststeq(CPUPPCState *env, uint32_t op1, uint32_t op2) { /* XXX: TODO: ignore special values (NaN, infinites, ...) */ return efscmpeq(env, op1, op2); } #define HELPER_SINGLE_SPE_CMP(name) \ uint32_t helper_e##name(CPUPPCState *env, uint32_t op1, uint32_t op2) \ { \ return e##name(env, op1, op2) << 2; \ } /* efststlt */ HELPER_SINGLE_SPE_CMP(fststlt); /* efststgt */ HELPER_SINGLE_SPE_CMP(fststgt); /* efststeq */ HELPER_SINGLE_SPE_CMP(fststeq); /* efscmplt */ HELPER_SINGLE_SPE_CMP(fscmplt); /* efscmpgt */ HELPER_SINGLE_SPE_CMP(fscmpgt); /* efscmpeq */ HELPER_SINGLE_SPE_CMP(fscmpeq); static inline uint32_t evcmp_merge(int t0, int t1) { return (t0 << 3) | (t1 << 2) | ((t0 | t1) << 1) | (t0 & t1); } #define HELPER_VECTOR_SPE_CMP(name) \ uint32_t helper_ev##name(CPUPPCState *env, uint64_t op1, uint64_t op2) \ { \ return evcmp_merge(e##name(env, op1 >> 32, op2 >> 32), \ e##name(env, op1, op2)); \ } /* evfststlt */ HELPER_VECTOR_SPE_CMP(fststlt); /* evfststgt */ HELPER_VECTOR_SPE_CMP(fststgt); /* evfststeq */ HELPER_VECTOR_SPE_CMP(fststeq); /* evfscmplt */ HELPER_VECTOR_SPE_CMP(fscmplt); /* evfscmpgt */ HELPER_VECTOR_SPE_CMP(fscmpgt); /* evfscmpeq */ HELPER_VECTOR_SPE_CMP(fscmpeq); /* Double-precision floating-point conversion */ uint64_t helper_efdcfsi(CPUPPCState *env, uint32_t val) { CPU_DoubleU u; u.d = int32_to_float64(val, &env->vec_status); return u.ll; } uint64_t helper_efdcfsid(CPUPPCState *env, uint64_t val) { CPU_DoubleU u; u.d = int64_to_float64(val, &env->vec_status); return u.ll; } uint64_t helper_efdcfui(CPUPPCState *env, uint32_t val) { CPU_DoubleU u; u.d = uint32_to_float64(val, &env->vec_status); return u.ll; } uint64_t helper_efdcfuid(CPUPPCState *env, uint64_t val) { CPU_DoubleU u; u.d = uint64_to_float64(val, &env->vec_status); return u.ll; } uint32_t helper_efdctsi(CPUPPCState *env, uint64_t val) { CPU_DoubleU u; u.ll = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float64_is_any_nan(u.d))) { return 0; } return float64_to_int32(u.d, &env->vec_status); } uint32_t helper_efdctui(CPUPPCState *env, uint64_t val) { CPU_DoubleU u; u.ll = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float64_is_any_nan(u.d))) { return 0; } return float64_to_uint32(u.d, &env->vec_status); } uint32_t helper_efdctsiz(CPUPPCState *env, uint64_t val) { CPU_DoubleU u; u.ll = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float64_is_any_nan(u.d))) { return 0; } return float64_to_int32_round_to_zero(u.d, &env->vec_status); } uint64_t helper_efdctsidz(CPUPPCState *env, uint64_t val) { CPU_DoubleU u; u.ll = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float64_is_any_nan(u.d))) { return 0; } return float64_to_int64_round_to_zero(u.d, &env->vec_status); } uint32_t helper_efdctuiz(CPUPPCState *env, uint64_t val) { CPU_DoubleU u; u.ll = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float64_is_any_nan(u.d))) { return 0; } return float64_to_uint32_round_to_zero(u.d, &env->vec_status); } uint64_t helper_efdctuidz(CPUPPCState *env, uint64_t val) { CPU_DoubleU u; u.ll = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float64_is_any_nan(u.d))) { return 0; } return float64_to_uint64_round_to_zero(u.d, &env->vec_status); } uint64_t helper_efdcfsf(CPUPPCState *env, uint32_t val) { CPU_DoubleU u; float64 tmp; u.d = int32_to_float64(val, &env->vec_status); tmp = int64_to_float64(1ULL << 32, &env->vec_status); u.d = float64_div(u.d, tmp, &env->vec_status); return u.ll; } uint64_t helper_efdcfuf(CPUPPCState *env, uint32_t val) { CPU_DoubleU u; float64 tmp; u.d = uint32_to_float64(val, &env->vec_status); tmp = int64_to_float64(1ULL << 32, &env->vec_status); u.d = float64_div(u.d, tmp, &env->vec_status); return u.ll; } uint32_t helper_efdctsf(CPUPPCState *env, uint64_t val) { CPU_DoubleU u; float64 tmp; u.ll = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float64_is_any_nan(u.d))) { return 0; } tmp = uint64_to_float64(1ULL << 32, &env->vec_status); u.d = float64_mul(u.d, tmp, &env->vec_status); return float64_to_int32(u.d, &env->vec_status); } uint32_t helper_efdctuf(CPUPPCState *env, uint64_t val) { CPU_DoubleU u; float64 tmp; u.ll = val; /* NaN are not treated the same way IEEE 754 does */ if (unlikely(float64_is_any_nan(u.d))) { return 0; } tmp = uint64_to_float64(1ULL << 32, &env->vec_status); u.d = float64_mul(u.d, tmp, &env->vec_status); return float64_to_uint32(u.d, &env->vec_status); } uint32_t helper_efscfd(CPUPPCState *env, uint64_t val) { CPU_DoubleU u1; CPU_FloatU u2; u1.ll = val; u2.f = float64_to_float32(u1.d, &env->vec_status); return u2.l; } uint64_t helper_efdcfs(CPUPPCState *env, uint32_t val) { CPU_DoubleU u2; CPU_FloatU u1; u1.l = val; u2.d = float32_to_float64(u1.f, &env->vec_status); return u2.ll; } /* Double precision fixed-point arithmetic */ uint64_t helper_efdadd(CPUPPCState *env, uint64_t op1, uint64_t op2) { CPU_DoubleU u1, u2; u1.ll = op1; u2.ll = op2; u1.d = float64_add(u1.d, u2.d, &env->vec_status); return u1.ll; } uint64_t helper_efdsub(CPUPPCState *env, uint64_t op1, uint64_t op2) { CPU_DoubleU u1, u2; u1.ll = op1; u2.ll = op2; u1.d = float64_sub(u1.d, u2.d, &env->vec_status); return u1.ll; } uint64_t helper_efdmul(CPUPPCState *env, uint64_t op1, uint64_t op2) { CPU_DoubleU u1, u2; u1.ll = op1; u2.ll = op2; u1.d = float64_mul(u1.d, u2.d, &env->vec_status); return u1.ll; } uint64_t helper_efddiv(CPUPPCState *env, uint64_t op1, uint64_t op2) { CPU_DoubleU u1, u2; u1.ll = op1; u2.ll = op2; u1.d = float64_div(u1.d, u2.d, &env->vec_status); return u1.ll; } /* Double precision floating point helpers */ uint32_t helper_efdtstlt(CPUPPCState *env, uint64_t op1, uint64_t op2) { CPU_DoubleU u1, u2; u1.ll = op1; u2.ll = op2; return float64_lt(u1.d, u2.d, &env->vec_status) ? 4 : 0; } uint32_t helper_efdtstgt(CPUPPCState *env, uint64_t op1, uint64_t op2) { CPU_DoubleU u1, u2; u1.ll = op1; u2.ll = op2; return float64_le(u1.d, u2.d, &env->vec_status) ? 0 : 4; } uint32_t helper_efdtsteq(CPUPPCState *env, uint64_t op1, uint64_t op2) { CPU_DoubleU u1, u2; u1.ll = op1; u2.ll = op2; return float64_eq_quiet(u1.d, u2.d, &env->vec_status) ? 4 : 0; } uint32_t helper_efdcmplt(CPUPPCState *env, uint64_t op1, uint64_t op2) { /* XXX: TODO: test special values (NaN, infinites, ...) */ return helper_efdtstlt(env, op1, op2); } uint32_t helper_efdcmpgt(CPUPPCState *env, uint64_t op1, uint64_t op2) { /* XXX: TODO: test special values (NaN, infinites, ...) */ return helper_efdtstgt(env, op1, op2); } uint32_t helper_efdcmpeq(CPUPPCState *env, uint64_t op1, uint64_t op2) { /* XXX: TODO: test special values (NaN, infinites, ...) */ return helper_efdtsteq(env, op1, op2); }