From 7c58044c0ab79f11604f71aa04b4691baacef886 Mon Sep 17 00:00:00 2001 From: j_mayer Date: Sat, 27 Oct 2007 17:54:30 +0000 Subject: [PATCH] Fix PowerPC FPSCR update and floating-point exception generation in most useful cases. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3458 c046a42c-6fe2-441c-8c8c-71466251a162 --- darwin-user/main.c | 7 +- linux-user/main.c | 8 +- target-ppc/cpu.h | 78 +++- target-ppc/helper.c | 13 +- target-ppc/op.c | 130 +++++- target-ppc/op_helper.c | 911 +++++++++++++++++++++++++++++++-------- target-ppc/op_helper.h | 15 +- target-ppc/op_template.h | 33 -- target-ppc/translate.c | 272 +++++++----- 9 files changed, 1120 insertions(+), 347 deletions(-) diff --git a/darwin-user/main.c b/darwin-user/main.c index d0de491e0b..70328ec8ee 100644 --- a/darwin-user/main.c +++ b/darwin-user/main.c @@ -224,11 +224,6 @@ void cpu_loop(CPUPPCState *env) case POWERPC_EXCP_FP: EXCP_DUMP(env, "Floating point program exception\n"); /* Set FX */ - env->fpscr[7] |= 0x8; - /* Finally, update FEX */ - if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) & - ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3))) - env->fpscr[7] |= 0x4; info.si_signo = SIGFPE; info.si_errno = 0; switch (env->error_code & 0xF) { @@ -248,7 +243,7 @@ void cpu_loop(CPUPPCState *env) case POWERPC_EXCP_FP_VXSOFT: info.si_code = FPE_FLTINV; break; - case POWERPC_EXCP_FP_VXNAN: + case POWERPC_EXCP_FP_VXSNAN: case POWERPC_EXCP_FP_VXISI: case POWERPC_EXCP_FP_VXIDI: case POWERPC_EXCP_FP_VXIMZ: diff --git a/linux-user/main.c b/linux-user/main.c index cac9561ba1..88a2e48863 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -829,12 +829,6 @@ void cpu_loop(CPUPPCState *env) switch (env->error_code & ~0xF) { case POWERPC_EXCP_FP: EXCP_DUMP(env, "Floating point program exception\n"); - /* Set FX */ - env->fpscr[7] |= 0x8; - /* Finally, update FEX */ - if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) & - ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3))) - env->fpscr[7] |= 0x4; info.si_signo = TARGET_SIGFPE; info.si_errno = 0; switch (env->error_code & 0xF) { @@ -854,7 +848,7 @@ void cpu_loop(CPUPPCState *env) case POWERPC_EXCP_FP_VXSOFT: info.si_code = TARGET_FPE_FLTINV; break; - case POWERPC_EXCP_FP_VXNAN: + case POWERPC_EXCP_FP_VXSNAN: case POWERPC_EXCP_FP_VXISI: case POWERPC_EXCP_FP_VXIDI: case POWERPC_EXCP_FP_VXIMZ: diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index fc1d9bbca3..196f98cae5 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -239,7 +239,7 @@ enum { POWERPC_EXCP_FP_UX = 0x02, /* FP underflow */ POWERPC_EXCP_FP_ZX = 0x03, /* FP divide by zero */ POWERPC_EXCP_FP_XX = 0x04, /* FP inexact */ - POWERPC_EXCP_FP_VXNAN = 0x05, /* FP invalid SNaN op */ + POWERPC_EXCP_FP_VXSNAN = 0x05, /* FP invalid SNaN op */ POWERPC_EXCP_FP_VXISI = 0x06, /* FP invalid infinite subtraction */ POWERPC_EXCP_FP_VXIDI = 0x07, /* FP invalid infinite divide */ POWERPC_EXCP_FP_VXZDZ = 0x08, /* FP invalid zero divide */ @@ -433,14 +433,84 @@ enum { POWERPC_FLAG_PMM = 0x00000400, }; +/*****************************************************************************/ +/* Floating point status and control register */ +#define FPSCR_FX 31 /* Floating-point exception summary */ +#define FPSCR_FEX 30 /* Floating-point enabled exception summary */ +#define FPSCR_VX 29 /* Floating-point invalid operation exception summ. */ +#define FPSCR_OX 28 /* Floating-point overflow exception */ +#define FPSCR_UX 27 /* Floating-point underflow exception */ +#define FPSCR_ZX 26 /* Floating-point zero divide exception */ +#define FPSCR_XX 25 /* Floating-point inexact exception */ +#define FPSCR_VXSNAN 24 /* Floating-point invalid operation exception (sNan) */ +#define FPSCR_VXISI 23 /* Floating-point invalid operation exception (inf) */ +#define FPSCR_VXIDI 22 /* Floating-point invalid operation exception (inf) */ +#define FPSCR_VXZDZ 21 /* Floating-point invalid operation exception (zero) */ +#define FPSCR_VXIMZ 20 /* Floating-point invalid operation exception (inf) */ +#define FPSCR_VXVC 19 /* Floating-point invalid operation exception (comp) */ +#define FPSCR_FR 18 /* Floating-point fraction rounded */ +#define FPSCR_FI 17 /* Floating-point fraction inexact */ +#define FPSCR_C 16 /* Floating-point result class descriptor */ +#define FPSCR_FL 15 /* Floating-point less than or negative */ +#define FPSCR_FG 14 /* Floating-point greater than or negative */ +#define FPSCR_FE 13 /* Floating-point equal or zero */ +#define FPSCR_FU 12 /* Floating-point unordered or NaN */ +#define FPSCR_FPCC 12 /* Floating-point condition code */ +#define FPSCR_FPRF 12 /* Floating-point result flags */ +#define FPSCR_VXSOFT 10 /* Floating-point invalid operation exception (soft) */ +#define FPSCR_VXSQRT 9 /* Floating-point invalid operation exception (sqrt) */ +#define FPSCR_VXCVI 8 /* Floating-point invalid operation exception (int) */ +#define FPSCR_VE 7 /* Floating-point invalid operation exception enable */ +#define FPSCR_OE 6 /* Floating-point overflow exception enable */ +#define FPSCR_UE 5 /* Floating-point undeflow exception enable */ +#define FPSCR_ZE 4 /* Floating-point zero divide exception enable */ +#define FPSCR_XE 3 /* Floating-point inexact exception enable */ +#define FPSCR_NI 2 /* Floating-point non-IEEE mode */ +#define FPSCR_RN1 1 +#define FPSCR_RN 0 /* Floating-point rounding control */ +#define fpscr_fex (((env->fpscr) >> FPSCR_FEX) & 0x1) +#define fpscr_vx (((env->fpscr) >> FPSCR_VX) & 0x1) +#define fpscr_ox (((env->fpscr) >> FPSCR_OX) & 0x1) +#define fpscr_ux (((env->fpscr) >> FPSCR_UX) & 0x1) +#define fpscr_zx (((env->fpscr) >> FPSCR_ZX) & 0x1) +#define fpscr_xx (((env->fpscr) >> FPSCR_XX) & 0x1) +#define fpscr_vxsnan (((env->fpscr) >> FPSCR_VXSNAN) & 0x1) +#define fpscr_vxisi (((env->fpscr) >> FPSCR_VXISI) & 0x1) +#define fpscr_vxidi (((env->fpscr) >> FPSCR_VXIDI) & 0x1) +#define fpscr_vxzdz (((env->fpscr) >> FPSCR_VXZDZ) & 0x1) +#define fpscr_vximz (((env->fpscr) >> FPSCR_VXIMZ) & 0x1) +#define fpscr_vxvc (((env->fpscr) >> FPSCR_VXVC) & 0x1) +#define fpscr_fpcc (((env->fpscr) >> FPSCR_FPCC) & 0xF) +#define fpscr_vxsoft (((env->fpscr) >> FPSCR_VXSOFT) & 0x1) +#define fpscr_vxsqrt (((env->fpscr) >> FPSCR_VXSQRT) & 0x1) +#define fpscr_vxcvi (((env->fpscr) >> FPSCR_VXCVI) & 0x1) +#define fpscr_ve (((env->fpscr) >> FPSCR_VE) & 0x1) +#define fpscr_oe (((env->fpscr) >> FPSCR_OE) & 0x1) +#define fpscr_ue (((env->fpscr) >> FPSCR_UE) & 0x1) +#define fpscr_ze (((env->fpscr) >> FPSCR_ZE) & 0x1) +#define fpscr_xe (((env->fpscr) >> FPSCR_XE) & 0x1) +#define fpscr_ni (((env->fpscr) >> FPSCR_NI) & 0x1) +#define fpscr_rn (((env->fpscr) >> FPSCR_RN) & 0x3) +/* Invalid operation exception summary */ +#define fpscr_ix ((env->fpscr) & ((1 << FPSCR_VXSNAN) | (1 << FPSCR_VXISI) | \ + (1 << FPSCR_VXIDI) | (1 << FPSCR_VXZDZ) | \ + (1 << FPSCR_VXIMZ) | (1 << FPSCR_VXVC) | \ + (1 << FPSCR_VXSOFT) | (1 << FPSCR_VXSQRT) | \ + (1 << FPSCR_VXCVI))) +/* exception summary */ +#define fpscr_ex (((env->fpscr) >> FPSCR_XX) & 0x1F) +/* enabled exception summary */ +#define fpscr_eex (((env->fpscr) >> FPSCR_XX) & ((env->fpscr) >> FPSCR_XE) & \ + 0x1F) + +/*****************************************************************************/ +/* The whole PowerPC CPU context */ #if defined(TARGET_PPC64H) #define NB_MMU_MODES 3 #else #define NB_MMU_MODES 2 #endif -/*****************************************************************************/ -/* The whole PowerPC CPU context */ struct CPUPPCState { /* First are the most commonly used resources * during translated code execution @@ -482,7 +552,7 @@ struct CPUPPCState { /* floating point registers */ float64 fpr[32]; /* floating point status and control register */ - uint8_t fpscr[8]; + uint32_t fpscr; CPU_COMMON diff --git a/target-ppc/helper.c b/target-ppc/helper.c index 9bdd8835e9..869d5b7677 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -2130,6 +2130,8 @@ static always_inline void powerpc_excp (CPUState *env, fprintf(logfile, "Ignore floating point exception\n"); } #endif + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; return; } new_msr &= ~((target_ulong)1 << MSR_RI); @@ -2138,12 +2140,6 @@ static always_inline void powerpc_excp (CPUState *env, new_msr |= (target_ulong)1 << MSR_HV; #endif msr |= 0x00100000; - /* Set FX */ - env->fpscr[7] |= 0x8; - /* Finally, update FEX */ - if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) & - ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3))) - env->fpscr[7] |= 0x4; if (msr_fe0 != msr_fe1) { msr |= 0x00010000; goto store_current; @@ -2199,8 +2195,11 @@ static always_inline void powerpc_excp (CPUState *env, /* XXX: To be removed */ if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b && env->osi_call) { - if (env->osi_call(env) != 0) + if (env->osi_call(env) != 0) { + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; return; + } } if (loglevel & CPU_LOG_INT) { dump_syscall(env); diff --git a/target-ppc/op.c b/target-ppc/op.c index 0495879ce3..0146d33c3d 100644 --- a/target-ppc/op.c +++ b/target-ppc/op.c @@ -135,13 +135,6 @@ void OPPROTO op_set_Rc0 (void) RETURN(); } -/* Set Rc1 (for floating point arithmetic) */ -void OPPROTO op_set_Rc1 (void) -{ - env->crf[1] = env->fpscr[7]; - RETURN(); -} - /* Constants load */ void OPPROTO op_reset_T0 (void) { @@ -552,9 +545,102 @@ void OPPROTO op_store_dbatl (void) #endif /* !defined(CONFIG_USER_ONLY) */ /* FPSCR */ -void OPPROTO op_load_fpscr (void) +#ifdef CONFIG_SOFTFLOAT +void OPPROTO op_reset_fpstatus (void) { - do_load_fpscr(); + env->fp_status.float_exception_flags = 0; + RETURN(); +} +#endif + +void OPPROTO op_compute_fprf (void) +{ + do_compute_fprf(PARAM1); + RETURN(); +} + +#ifdef CONFIG_SOFTFLOAT +void OPPROTO op_float_check_status (void) +{ + do_float_check_status(); + RETURN(); +} +#else +void OPPROTO op_float_check_status (void) +{ + 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) + do_raise_exception_err(env->exception_index, env->error_code); + } + RETURN(); +} +#endif + +#if defined(WORDS_BIGENDIAN) +#define WORD0 0 +#define WORD1 1 +#else +#define WORD0 1 +#define WORD1 0 +#endif +void OPPROTO op_load_fpscr_FT0 (void) +{ + /* The 32 MSB of the target fpr are undefined. + * They'll be zero... + */ + union { + float64 d; + struct { + uint32_t u[2]; + } s; + } u; + + u.s.u[WORD0] = 0; + u.s.u[WORD1] = env->fpscr; + FT0 = u.d; + RETURN(); +} + +void OPPROTO op_set_FT0 (void) +{ + union { + float64 d; + struct { + uint32_t u[2]; + } s; + } u; + + u.s.u[WORD0] = 0; + u.s.u[WORD1] = PARAM1; + FT0 = u.d; + RETURN(); +} +#undef WORD0 +#undef WORD1 + +void OPPROTO op_load_fpscr_T0 (void) +{ + T0 = (env->fpscr >> PARAM1) & 0xF; + RETURN(); +} + +void OPPROTO op_load_fpcc (void) +{ + T0 = fpscr_fpcc; + RETURN(); +} + +void OPPROTO op_fpscr_resetbit (void) +{ + env->fpscr &= PARAM1; + RETURN(); +} + +void OPPROTO op_fpscr_setbit (void) +{ + do_fpscr_setbit(PARAM1); RETURN(); } @@ -564,12 +650,6 @@ void OPPROTO op_store_fpscr (void) RETURN(); } -void OPPROTO op_reset_scrfx (void) -{ - env->fpscr[7] &= ~0x8; - RETURN(); -} - /* crf operations */ void OPPROTO op_getbit_T0 (void) { @@ -1702,28 +1782,44 @@ void OPPROTO op_srli_T1_64 (void) /* fadd - fadd. */ void OPPROTO op_fadd (void) { +#if USE_PRECISE_EMULATION + do_fadd(); +#else FT0 = float64_add(FT0, FT1, &env->fp_status); +#endif RETURN(); } /* fsub - fsub. */ void OPPROTO op_fsub (void) { +#if USE_PRECISE_EMULATION + do_fsub(); +#else FT0 = float64_sub(FT0, FT1, &env->fp_status); +#endif RETURN(); } /* fmul - fmul. */ void OPPROTO op_fmul (void) { +#if USE_PRECISE_EMULATION + do_fmul(); +#else FT0 = float64_mul(FT0, FT1, &env->fp_status); +#endif RETURN(); } /* fdiv - fdiv. */ void OPPROTO op_fdiv (void) { +#if USE_PRECISE_EMULATION + do_fdiv(); +#else FT0 = float64_div(FT0, FT1, &env->fp_status); +#endif RETURN(); } @@ -1805,7 +1901,11 @@ void OPPROTO op_fnmsub (void) /* frsp - frsp. */ void OPPROTO op_frsp (void) { +#if USE_PRECISE_EMULATION + do_frsp(); +#else FT0 = float64_to_float32(FT0, &env->fp_status); +#endif RETURN(); } diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c index 06a5a561ca..8182773564 100644 --- a/target-ppc/op_helper.c +++ b/target-ppc/op_helper.c @@ -51,14 +51,6 @@ void do_raise_exception_err (uint32_t exception, int error_code) #if 0 printf("Raise exception %3x code : %d\n", exception, error_code); #endif - switch (exception) { - case POWERPC_EXCP_PROGRAM: - if (error_code == POWERPC_EXCP_FP && msr_fe0 == 0 && msr_fe1 == 0) - return; - break; - default: - break; - } env->exception_index = exception; env->error_code = error_code; cpu_loop_exit(); @@ -107,77 +99,6 @@ void do_store_pri (int prio) } #endif -void do_load_fpscr (void) -{ - /* The 32 MSB of the target fpr are undefined. - * They'll be zero... - */ - union { - float64 d; - struct { - uint32_t u[2]; - } s; - } u; - int i; - -#if defined(WORDS_BIGENDIAN) -#define WORD0 0 -#define WORD1 1 -#else -#define WORD0 1 -#define WORD1 0 -#endif - u.s.u[WORD0] = 0; - u.s.u[WORD1] = 0; - for (i = 0; i < 8; i++) - u.s.u[WORD1] |= env->fpscr[i] << (4 * i); - FT0 = u.d; -} - -void do_store_fpscr (uint32_t mask) -{ - /* - * We use only the 32 LSB of the incoming fpr - */ - union { - double d; - struct { - uint32_t u[2]; - } s; - } u; - int i, rnd_type; - - u.d = FT0; - if (mask & 0x80) - env->fpscr[0] = (env->fpscr[0] & 0x9) | ((u.s.u[WORD1] >> 28) & ~0x9); - for (i = 1; i < 7; i++) { - if (mask & (1 << (7 - i))) - env->fpscr[i] = (u.s.u[WORD1] >> (4 * (7 - i))) & 0xF; - } - /* TODO: update FEX & VX */ - /* Set rounding mode */ - switch (env->fpscr[0] & 0x3) { - 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); -} - target_ulong ppc_load_dump_spr (int sprn) { if (loglevel != 0) { @@ -553,6 +474,538 @@ void do_popcntb_64 (void) /*****************************************************************************/ /* Floating point operations helpers */ +static inline int fpisneg (float64 f) +{ + union { + float64 f; + uint64_t u; + } u; + + u.f = f; + + return u.u >> 63 != 0; +} + +static inline int isden (float f) +{ + union { + float64 f; + uint64_t u; + } u; + + u.f = f; + + return ((u.u >> 52) & 0x7FF) == 0; +} + +static inline int iszero (float64 f) +{ + union { + float64 f; + uint64_t u; + } u; + + u.f = f; + + return (u.u & ~0x8000000000000000ULL) == 0; +} + +static inline int isinfinity (float64 f) +{ + union { + float64 f; + uint64_t u; + } u; + + u.f = f; + + return ((u.u >> 51) & 0x3FF) == 0x3FF && + (u.u & 0x000FFFFFFFFFFFFFULL) == 0; +} + +void do_compute_fprf (int set_fprf) +{ + int isneg; + + isneg = fpisneg(FT0); + if (unlikely(float64_is_nan(FT0))) { + if (float64_is_signaling_nan(FT0)) { + /* Signaling NaN: flags are undefined */ + T0 = 0x00; + } else { + /* Quiet NaN */ + T0 = 0x11; + } + } else if (unlikely(isinfinity(FT0))) { + /* +/- infinity */ + if (isneg) + T0 = 0x09; + else + T0 = 0x05; + } else { + if (iszero(FT0)) { + /* +/- zero */ + if (isneg) + T0 = 0x12; + else + T0 = 0x02; + } else { + if (isden(FT0)) { + /* Denormalized numbers */ + T0 = 0x10; + } else { + /* Normalized numbers */ + T0 = 0x00; + } + if (isneg) { + T0 |= 0x08; + } else { + T0 |= 0x04; + } + } + } + if (set_fprf) { + /* We update FPSCR_FPRF */ + env->fpscr &= ~(0x1F << FPSCR_FPRF); + env->fpscr |= T0 << FPSCR_FPRF; + } + /* We just need fpcc to update Rc1 */ + T0 &= 0xF; +} + +/* Floating-point invalid operations exception */ +static always_inline void fload_invalid_op_excp (int op) +{ + int ve; + + ve = fpscr_ve; + if (op & POWERPC_EXCP_FP_VXSNAN) { + /* Operation on signaling NaN */ + env->fpscr |= 1 << FPSCR_VXSNAN; + } + if (op & POWERPC_EXCP_FP_VXSOFT) { + /* Software-defined condition */ + env->fpscr |= 1 << FPSCR_VXSOFT; + } + switch (op & ~(POWERPC_EXCP_FP_VXSOFT | POWERPC_EXCP_FP_VXSNAN)) { + 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 */ + FT0 = (uint64_t)-1; + 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 */ + FT0 = (uint64_t)-1; + 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) + do_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_FP | op); + } +} + +static always_inline void float_zero_divide_excp (void) +{ + union { + float64 f; + uint64_t u; + } u0, u1; + + + 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) { + do_raise_exception_err(POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX); + } + } else { + /* Set the result to infinity */ + u0.f = FT0; + u1.f = FT1; + u0.u = ((u0.u ^ u1.u) & 0x8000000000000000ULL); + u0.u |= 0x3FFULL << 51; + FT0 = u0.f; + } +} + +static always_inline void float_overflow_excp (void) +{ + 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 always_inline void float_underflow_excp (void) +{ + 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 always_inline void float_inexact_excp (void) +{ + 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 always_inline void fpscr_set_rounding_mode (void) +{ + 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 do_fpscr_setbit (int 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; + 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(); + 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; + } + } +} + +#if defined(WORDS_BIGENDIAN) +#define WORD0 0 +#define WORD1 1 +#else +#define WORD0 1 +#define WORD1 0 +#endif +void do_store_fpscr (uint32_t mask) +{ + /* + * We use only the 32 LSB of the incoming fpr + */ + union { + double d; + struct { + uint32_t u[2]; + } s; + } u; + uint32_t prev, new; + int i; + + u.d = FT0; + prev = env->fpscr; + new = u.s.u[WORD1]; + new &= ~0x90000000; + new |= prev & 0x90000000; + for (i = 0; i < 7; 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; + 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; + } + fpscr_set_rounding_mode(); +} +#undef WORD0 +#undef WORD1 + +#ifdef CONFIG_SOFTFLOAT +void do_float_check_status (void) +{ + 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) + do_raise_exception_err(env->exception_index, env->error_code); + } else if (env->fp_status.float_exception_flags & float_flag_overflow) { + float_overflow_excp(); + } else if (env->fp_status.float_exception_flags & float_flag_underflow) { + float_underflow_excp(); + } else if (env->fp_status.float_exception_flags & float_flag_inexact) { + float_inexact_excp(); + } +} +#endif + +#if USE_PRECISE_EMULATION +void do_fadd (void) +{ + if (unlikely(float64_is_signaling_nan(FT0) || + float64_is_signaling_nan(FT1))) { + /* sNaN addition */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else if (likely(isfinite(FT0) || isfinite(FT1) || + fpisneg(FT0) == fpisneg(FT1))) { + FT0 = float64_add(FT0, FT1, &env->fp_status); + } else { + /* Magnitude subtraction of infinities */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); + } +} + +void do_fsub (void) +{ + if (unlikely(float64_is_signaling_nan(FT0) || + float64_is_signaling_nan(FT1))) { + /* sNaN subtraction */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else if (likely(isfinite(FT0) || isfinite(FT1) || + fpisneg(FT0) != fpisneg(FT1))) { + FT0 = float64_sub(FT0, FT1, &env->fp_status); + } else { + /* Magnitude subtraction of infinities */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); + } +} + +void do_fmul (void) +{ + if (unlikely(float64_is_signaling_nan(FT0) || + float64_is_signaling_nan(FT1))) { + /* sNaN multiplication */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else if (unlikely((ifinf(FT0) && iszero(FT1)) || + (inzero(FT0) && isinfinity(FT1)))) { + /* Multiplication of zero by infinity */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); + } else { + FT0 = float64_mul(FT0, FT1, &env->fp_status); + } +} + +void do_fdiv (void) +{ + if (unlikely(float64_is_signaling_nan(FT0) || + float64_is_signaling_nan(FT1))) { + /* sNaN division */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else if (unlikely(isinfinity(FT0) && isinfinity(FT1))) { + /* Division of infinity by infinity */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXIDI); + } else if (unlikely(iszero(FT1))) { + if (iszero(FT0)) { + /* Division of zero by zero */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXZDZ); + } else { + /* Division by zero */ + float_zero_divide_excp(); + } + } else { + FT0 = float64_div(FT0, FT1, &env->fp_status); + } +} +#endif /* USE_PRECISE_EMULATION */ + void do_fctiw (void) { union { @@ -560,14 +1013,22 @@ void do_fctiw (void) uint64_t i; } p; - p.i = float64_to_int32(FT0, &env->fp_status); + if (unlikely(float64_is_signaling_nan(FT0))) { + /* sNaN conversion */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); + } else if (unlikely(float64_is_nan(FT0) || isinfinity(FT0))) { + /* qNan / infinity conversion */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); + } else { + p.i = float64_to_int32(FT0, &env->fp_status); #if USE_PRECISE_EMULATION - /* XXX: higher bits are not supposed to be significant. - * to make tests easier, return the same as a real PowerPC 750 (aka G3) - */ - p.i |= 0xFFF80000ULL << 32; + /* XXX: higher bits are not supposed to be significant. + * to make tests easier, return the same as a real PowerPC 750 + */ + p.i |= 0xFFF80000ULL << 32; #endif - FT0 = p.d; + FT0 = p.d; + } } void do_fctiwz (void) @@ -577,14 +1038,22 @@ void do_fctiwz (void) uint64_t i; } p; - p.i = float64_to_int32_round_to_zero(FT0, &env->fp_status); + if (unlikely(float64_is_signaling_nan(FT0))) { + /* sNaN conversion */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); + } else if (unlikely(float64_is_nan(FT0) || isinfinity(FT0))) { + /* qNan / infinity conversion */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); + } else { + p.i = float64_to_int32_round_to_zero(FT0, &env->fp_status); #if USE_PRECISE_EMULATION - /* XXX: higher bits are not supposed to be significant. - * to make tests easier, return the same as a real PowerPC 750 (aka G3) - */ - p.i |= 0xFFF80000ULL << 32; + /* XXX: higher bits are not supposed to be significant. + * to make tests easier, return the same as a real PowerPC 750 + */ + p.i |= 0xFFF80000ULL << 32; #endif - FT0 = p.d; + FT0 = p.d; + } } #if defined(TARGET_PPC64) @@ -606,8 +1075,16 @@ void do_fctid (void) uint64_t i; } p; - p.i = float64_to_int64(FT0, &env->fp_status); - FT0 = p.d; + if (unlikely(float64_is_signaling_nan(FT0))) { + /* sNaN conversion */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); + } else if (unlikely(float64_is_nan(FT0) || isinfinity(FT0))) { + /* qNan / infinity conversion */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); + } else { + p.i = float64_to_int64(FT0, &env->fp_status); + FT0 = p.d; + } } void do_fctidz (void) @@ -617,20 +1094,34 @@ void do_fctidz (void) uint64_t i; } p; - p.i = float64_to_int64_round_to_zero(FT0, &env->fp_status); - FT0 = p.d; + if (unlikely(float64_is_signaling_nan(FT0))) { + /* sNaN conversion */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); + } else if (unlikely(float64_is_nan(FT0) || isinfinity(FT0))) { + /* qNan / infinity conversion */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); + } else { + p.i = float64_to_int64_round_to_zero(FT0, &env->fp_status); + FT0 = p.d; + } } #endif static always_inline void do_fri (int rounding_mode) { - int curmode; - - curmode = env->fp_status.float_rounding_mode; - set_float_rounding_mode(rounding_mode, &env->fp_status); - FT0 = float64_round_to_int(FT0, &env->fp_status); - set_float_rounding_mode(curmode, &env->fp_status); + if (unlikely(float64_is_signaling_nan(FT0))) { + /* sNaN round */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); + } else if (unlikely(float64_is_nan(FT0) || isinfinity(FT0))) { + /* qNan / infinity round */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); + } else { + set_float_rounding_mode(rounding_mode, &env->fp_status); + FT0 = float64_round_to_int(FT0, &env->fp_status); + /* Restore rounding mode from FPSCR */ + fpscr_set_rounding_mode(); + } } void do_frin (void) @@ -656,90 +1147,142 @@ void do_frim (void) #if USE_PRECISE_EMULATION void do_fmadd (void) { + if (unlikely(float64_is_signaling_nan(FT0) || + float64_is_signaling_nan(FT1) || + float64_is_signaling_nan(FT2))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else { #ifdef FLOAT128 - float128 ft0_128, ft1_128; + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; - ft0_128 = float64_to_float128(FT0, &env->fp_status); - ft1_128 = float64_to_float128(FT1, &env->fp_status); - ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); - ft1_128 = float64_to_float128(FT2, &env->fp_status); - ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); - FT0 = float128_to_float64(ft0_128, &env->fp_status); + ft0_128 = float64_to_float128(FT0, &env->fp_status); + ft1_128 = float64_to_float128(FT1, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + ft1_128 = float64_to_float128(FT2, &env->fp_status); + ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); + FT0 = float128_to_float64(ft0_128, &env->fp_status); #else - /* This is OK on x86 hosts */ - FT0 = (FT0 * FT1) + FT2; + /* This is OK on x86 hosts */ + FT0 = (FT0 * FT1) + FT2; #endif + } } void do_fmsub (void) { + if (unlikely(float64_is_signaling_nan(FT0) || + float64_is_signaling_nan(FT1) || + float64_is_signaling_nan(FT2))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else { #ifdef FLOAT128 - float128 ft0_128, ft1_128; + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; - ft0_128 = float64_to_float128(FT0, &env->fp_status); - ft1_128 = float64_to_float128(FT1, &env->fp_status); - ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); - ft1_128 = float64_to_float128(FT2, &env->fp_status); - ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); - FT0 = float128_to_float64(ft0_128, &env->fp_status); + ft0_128 = float64_to_float128(FT0, &env->fp_status); + ft1_128 = float64_to_float128(FT1, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + ft1_128 = float64_to_float128(FT2, &env->fp_status); + ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); + FT0 = float128_to_float64(ft0_128, &env->fp_status); #else - /* This is OK on x86 hosts */ - FT0 = (FT0 * FT1) - FT2; + /* This is OK on x86 hosts */ + FT0 = (FT0 * FT1) - FT2; #endif + } } #endif /* USE_PRECISE_EMULATION */ void do_fnmadd (void) { + if (unlikely(float64_is_signaling_nan(FT0) || + float64_is_signaling_nan(FT1) || + float64_is_signaling_nan(FT2))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else { #if USE_PRECISE_EMULATION #ifdef FLOAT128 - float128 ft0_128, ft1_128; + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; - ft0_128 = float64_to_float128(FT0, &env->fp_status); - ft1_128 = float64_to_float128(FT1, &env->fp_status); - ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); - ft1_128 = float64_to_float128(FT2, &env->fp_status); - ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); - FT0 = float128_to_float64(ft0_128, &env->fp_status); + ft0_128 = float64_to_float128(FT0, &env->fp_status); + ft1_128 = float64_to_float128(FT1, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + ft1_128 = float64_to_float128(FT2, &env->fp_status); + ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status); + FT0 = float128_to_float64(ft0_128, &env->fp_status); #else - /* This is OK on x86 hosts */ - FT0 = (FT0 * FT1) + FT2; + /* This is OK on x86 hosts */ + FT0 = (FT0 * FT1) + FT2; #endif #else - FT0 = float64_mul(FT0, FT1, &env->fp_status); - FT0 = float64_add(FT0, FT2, &env->fp_status); + FT0 = float64_mul(FT0, FT1, &env->fp_status); + FT0 = float64_add(FT0, FT2, &env->fp_status); #endif - if (likely(!isnan(FT0))) - FT0 = float64_chs(FT0); + if (likely(!isnan(FT0))) + FT0 = float64_chs(FT0); + } } void do_fnmsub (void) { + if (unlikely(float64_is_signaling_nan(FT0) || + float64_is_signaling_nan(FT1) || + float64_is_signaling_nan(FT2))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else { #if USE_PRECISE_EMULATION #ifdef FLOAT128 - float128 ft0_128, ft1_128; + /* This is the way the PowerPC specification defines it */ + float128 ft0_128, ft1_128; - ft0_128 = float64_to_float128(FT0, &env->fp_status); - ft1_128 = float64_to_float128(FT1, &env->fp_status); - ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); - ft1_128 = float64_to_float128(FT2, &env->fp_status); - ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); - FT0 = float128_to_float64(ft0_128, &env->fp_status); + ft0_128 = float64_to_float128(FT0, &env->fp_status); + ft1_128 = float64_to_float128(FT1, &env->fp_status); + ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status); + ft1_128 = float64_to_float128(FT2, &env->fp_status); + ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status); + FT0 = float128_to_float64(ft0_128, &env->fp_status); #else - /* This is OK on x86 hosts */ - FT0 = (FT0 * FT1) - FT2; + /* This is OK on x86 hosts */ + FT0 = (FT0 * FT1) - FT2; #endif #else - FT0 = float64_mul(FT0, FT1, &env->fp_status); - FT0 = float64_sub(FT0, FT2, &env->fp_status); + FT0 = float64_mul(FT0, FT1, &env->fp_status); + FT0 = float64_sub(FT0, FT2, &env->fp_status); #endif - if (likely(!isnan(FT0))) - FT0 = float64_chs(FT0); + if (likely(!isnan(FT0))) + FT0 = float64_chs(FT0); + } } +#if USE_PRECISE_EMULATION +void do_frsp (void) +{ + if (unlikely(float64_is_signaling_nan(FT0))) { + /* sNaN square root */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else { + FT0 = float64_to_float32(FT0, &env->fp_status); + } +} +#endif /* USE_PRECISE_EMULATION */ + void do_fsqrt (void) { - FT0 = float64_sqrt(FT0, &env->fp_status); + if (unlikely(float64_is_signaling_nan(FT0))) { + /* sNaN square root */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else if (unlikely(fpisneg(FT0) && !iszero(FT0))) { + /* Square root of a negative nonzero number */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT); + } else { + FT0 = float64_sqrt(FT0, &env->fp_status); + } } void do_fre (void) @@ -749,7 +1292,13 @@ void do_fre (void) uint64_t i; } p; - if (likely(isnormal(FT0))) { + if (unlikely(float64_is_signaling_nan(FT0))) { + /* sNaN reciprocal */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else if (unlikely(iszero(FT0))) { + /* Zero reciprocal */ + float_zero_divide_excp(); + } else if (likely(isnormal(FT0))) { FT0 = float64_div(1.0, FT0, &env->fp_status); } else { p.d = FT0; @@ -759,7 +1308,7 @@ void do_fre (void) p.i = 0x7FF0000000000000ULL; } else if (isnan(FT0)) { p.i = 0x7FF8000000000000ULL; - } else if (FT0 < 0.0) { + } else if (fpisneg(FT0)) { p.i = 0x8000000000000000ULL; } else { p.i = 0x0000000000000000ULL; @@ -775,7 +1324,13 @@ void do_fres (void) uint64_t i; } p; - if (likely(isnormal(FT0))) { + if (unlikely(float64_is_signaling_nan(FT0))) { + /* sNaN reciprocal */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else if (unlikely(iszero(FT0))) { + /* Zero reciprocal */ + float_zero_divide_excp(); + } else if (likely(isnormal(FT0))) { #if USE_PRECISE_EMULATION FT0 = float64_div(1.0, FT0, &env->fp_status); FT0 = float64_to_float32(FT0, &env->fp_status); @@ -790,7 +1345,7 @@ void do_fres (void) p.i = 0x7FF0000000000000ULL; } else if (isnan(FT0)) { p.i = 0x7FF8000000000000ULL; - } else if (FT0 < 0.0) { + } else if (fpisneg(FT0)) { p.i = 0x8000000000000000ULL; } else { p.i = 0x0000000000000000ULL; @@ -806,7 +1361,13 @@ void do_frsqrte (void) uint64_t i; } p; - if (likely(isnormal(FT0) && FT0 > 0.0)) { + if (unlikely(float64_is_signaling_nan(FT0))) { + /* sNaN reciprocal square root */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else if (unlikely(fpisneg(FT0) && !iszero(FT0))) { + /* Reciprocal square root of a negative nonzero number */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT); + } else if (likely(isnormal(FT0))) { FT0 = float64_sqrt(FT0, &env->fp_status); FT0 = float32_div(1.0, FT0, &env->fp_status); } else { @@ -816,9 +1377,8 @@ void do_frsqrte (void) } else if (p.i == 0x0000000000000000ULL) { p.i = 0x7FF0000000000000ULL; } else if (isnan(FT0)) { - if (!(p.i & 0x0008000000000000ULL)) - p.i |= 0x000FFFFFFFFFFFFFULL; - } else if (FT0 < 0) { + p.i |= 0x000FFFFFFFFFFFFFULL; + } else if (fpisneg(FT0)) { p.i = 0x7FF8000000000000ULL; } else { p.i = 0x0000000000000000ULL; @@ -829,7 +1389,7 @@ void do_frsqrte (void) void do_fsel (void) { - if (FT0 >= 0) + if (!fpisneg(FT0) || iszero(FT0)) FT0 = FT1; else FT0 = FT2; @@ -837,7 +1397,11 @@ void do_fsel (void) void do_fcmpu (void) { - if (likely(!isnan(FT0) && !isnan(FT1))) { + if (unlikely(float64_is_signaling_nan(FT0) || + float64_is_signaling_nan(FT1))) { + /* sNaN comparison */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } else { if (float64_lt(FT0, FT1, &env->fp_status)) { T0 = 0x08UL; } else if (!float64_le(FT0, FT1, &env->fp_status)) { @@ -845,18 +1409,25 @@ void do_fcmpu (void) } else { T0 = 0x02UL; } - } else { - T0 = 0x01UL; - env->fpscr[4] |= 0x1; - env->fpscr[6] |= 0x1; } - env->fpscr[3] = T0; + env->fpscr &= ~(0x0F << FPSCR_FPRF); + env->fpscr |= T0 << FPSCR_FPRF; } void do_fcmpo (void) { - env->fpscr[4] &= ~0x1; - if (likely(!isnan(FT0) && !isnan(FT1))) { + if (unlikely(float64_is_nan(FT0) || + float64_is_nan(FT1))) { + if (float64_is_signaling_nan(FT0) || + float64_is_signaling_nan(FT1)) { + /* sNaN comparison */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | + POWERPC_EXCP_FP_VXVC); + } else { + /* qNaN comparison */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXVC); + } + } else { if (float64_lt(FT0, FT1, &env->fp_status)) { T0 = 0x08UL; } else if (!float64_le(FT0, FT1, &env->fp_status)) { @@ -864,19 +1435,9 @@ void do_fcmpo (void) } else { T0 = 0x02UL; } - } else { - T0 = 0x01UL; - env->fpscr[4] |= 0x1; - if (!float64_is_signaling_nan(FT0) || !float64_is_signaling_nan(FT1)) { - /* Quiet NaN case */ - env->fpscr[6] |= 0x1; - if (!(env->fpscr[1] & 0x8)) - env->fpscr[4] |= 0x8; - } else { - env->fpscr[4] |= 0x8; - } } - env->fpscr[3] = T0; + env->fpscr &= ~(0x0F << FPSCR_FPRF); + env->fpscr |= T0 << FPSCR_FPRF; } #if !defined (CONFIG_USER_ONLY) diff --git a/target-ppc/op_helper.h b/target-ppc/op_helper.h index d8b2f252c8..6597b3ca58 100644 --- a/target-ppc/op_helper.h +++ b/target-ppc/op_helper.h @@ -60,7 +60,7 @@ void do_store_cr (uint32_t mask); #if defined(TARGET_PPC64) void do_store_pri (int prio); #endif -void do_load_fpscr (void); +void do_fpscr_setbit (int bit); void do_store_fpscr (uint32_t mask); target_ulong ppc_load_dump_spr (int sprn); void ppc_store_dump_spr (int sprn, target_ulong val); @@ -94,6 +94,16 @@ void do_popcntb_64 (void); #endif /* Floating-point arithmetic helpers */ +void do_compute_fprf (int set_class); +#ifdef CONFIG_SOFTFLOAT +void do_float_check_status (void); +#endif +#if USE_PRECISE_EMULATION +void do_fadd (void); +void do_fsub (void); +void do_fmul (void); +void do_fdiv (void); +#endif void do_fsqrt (void); void do_fre (void); void do_fres (void); @@ -105,6 +115,9 @@ void do_fmsub (void); #endif void do_fnmadd (void); void do_fnmsub (void); +#if USE_PRECISE_EMULATION +void do_frsp (void); +#endif void do_fctiw (void); void do_fctiwz (void); #if defined(TARGET_PPC64) diff --git a/target-ppc/op_template.h b/target-ppc/op_template.h index 28dc59c942..bdd8844323 100644 --- a/target-ppc/op_template.h +++ b/target-ppc/op_template.h @@ -165,39 +165,6 @@ void OPPROTO glue(op_store_T1_crf_crf, REG) (void) RETURN(); } -/* Floating point condition and status register moves */ -void OPPROTO glue(op_load_fpscr_T0_fpscr, REG) (void) -{ - T0 = env->fpscr[REG]; - RETURN(); -} - -#if REG == 0 -void OPPROTO glue(op_store_T0_fpscr_fpscr, REG) (void) -{ - env->fpscr[REG] = (env->fpscr[REG] & 0x9) | (T0 & ~0x9); - RETURN(); -} - -void OPPROTO glue(op_clear_fpscr_fpscr, REG) (void) -{ - env->fpscr[REG] = (env->fpscr[REG] & 0x9); - RETURN(); -} -#else -void OPPROTO glue(op_store_T0_fpscr_fpscr, REG) (void) -{ - env->fpscr[REG] = T0; - RETURN(); -} - -void OPPROTO glue(op_clear_fpscr_fpscr, REG) (void) -{ - env->fpscr[REG] = 0x0; - RETURN(); -} -#endif - #endif /* REG <= 7 */ /* floating point registers moves */ diff --git a/target-ppc/translate.c b/target-ppc/translate.c index 4c5e8c6b98..3c05b2cbb5 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -32,6 +32,7 @@ //#define PPC_DEBUG_DISAS //#define DEBUG_MEMORY_ACCESSES //#define DO_PPC_STATISTICS +//#define OPTIMIZE_FPRF_UPDATE /*****************************************************************************/ /* Code translation helpers */ @@ -50,6 +51,10 @@ enum { static uint16_t *gen_opc_ptr; static uint32_t *gen_opparam_ptr; +#if defined(OPTIMIZE_FPRF_UPDATE) +static uint16_t *gen_fprf_buf[OPC_BUF_SIZE]; +static uint16_t **gen_fprf_ptr; +#endif #include "gen-op.h" @@ -117,16 +122,6 @@ GEN8(gen_op_load_crf_T1, gen_op_load_crf_T1_crf); GEN8(gen_op_store_T0_crf, gen_op_store_T0_crf_crf); GEN8(gen_op_store_T1_crf, gen_op_store_T1_crf_crf); -/* Floating point condition and status register moves */ -GEN8(gen_op_load_fpscr_T0, gen_op_load_fpscr_T0_fpscr); -GEN8(gen_op_store_T0_fpscr, gen_op_store_T0_fpscr_fpscr); -GEN8(gen_op_clear_fpscr, gen_op_clear_fpscr_fpscr); -static always_inline void gen_op_store_T0_fpscri (int n, uint8_t param) -{ - gen_op_set_T0(param); - gen_op_store_T0_fpscr(n); -} - /* General purpose registers moves */ GEN32(gen_op_load_gpr_T0, gen_op_load_gpr_T0_gpr); GEN32(gen_op_load_gpr_T1, gen_op_load_gpr_T1_gpr); @@ -199,6 +194,44 @@ static always_inline void gen_set_Rc0 (DisasContext *ctx) gen_op_set_Rc0(); } +static always_inline void gen_reset_fpstatus (void) +{ +#ifdef CONFIG_SOFTFLOAT + gen_op_reset_fpstatus(); +#endif +} + +static always_inline void gen_compute_fprf (int set_fprf, int set_rc) +{ + if (set_fprf != 0) { + /* This case might be optimized later */ +#if defined(OPTIMIZE_FPRF_UPDATE) + *gen_fprf_ptr++ = gen_opc_ptr; +#endif + gen_op_compute_fprf(1); + if (unlikely(set_rc)) + gen_op_store_T0_crf(1); + gen_op_float_check_status(); + } else if (unlikely(set_rc)) { + /* We always need to compute fpcc */ + gen_op_compute_fprf(0); + gen_op_store_T0_crf(1); + if (set_fprf) + gen_op_float_check_status(); + } +} + +static always_inline void gen_optimize_fprf (void) +{ +#if defined(OPTIMIZE_FPRF_UPDATE) + uint16_t **ptr; + + for (ptr = gen_fprf_buf; ptr != (gen_fprf_ptr - 1); ptr++) + *ptr = INDEX_op_nop1; + gen_fprf_ptr = gen_fprf_buf; +#endif +} + static always_inline void gen_update_nip (DisasContext *ctx, target_ulong nip) { #if defined(TARGET_PPC64) @@ -497,6 +530,8 @@ enum { PPC_CACHE_DCBZ = 0x0000400000000000ULL, /* dcbz instruction with tunable cache line size */ PPC_CACHE_DCBZT = 0x0000800000000000ULL, + /* frsqrtes extension */ + PPC_FLOAT_FRSQRTES = 0x0001000000000000ULL, }; /*****************************************************************************/ @@ -1656,124 +1691,127 @@ __GEN_LOGICAL2(srd, 0x1B, 0x10, PPC_64B); #endif /*** Floating-Point arithmetic ***/ -#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, type) \ +#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \ GEN_HANDLER(f##name, op1, op2, 0xFF, 0x00000000, type) \ { \ if (unlikely(!ctx->fpu_enabled)) { \ GEN_EXCP_NO_FP(ctx); \ return; \ } \ - gen_op_reset_scrfx(); \ gen_op_load_fpr_FT0(rA(ctx->opcode)); \ gen_op_load_fpr_FT1(rC(ctx->opcode)); \ gen_op_load_fpr_FT2(rB(ctx->opcode)); \ + gen_reset_fpstatus(); \ gen_op_f##op(); \ if (isfloat) { \ gen_op_frsp(); \ } \ gen_op_store_FT0_fpr(rD(ctx->opcode)); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_op_set_Rc1(); \ + gen_compute_fprf(set_fprf, Rc(ctx->opcode) != 0); \ } -#define GEN_FLOAT_ACB(name, op2, type) \ -_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, type); \ -_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, type); +#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ +_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, set_fprf, type); \ +_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type); -#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, inval, PPC_FLOAT) \ +#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type) \ +GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type) \ { \ if (unlikely(!ctx->fpu_enabled)) { \ GEN_EXCP_NO_FP(ctx); \ return; \ } \ - gen_op_reset_scrfx(); \ gen_op_load_fpr_FT0(rA(ctx->opcode)); \ gen_op_load_fpr_FT1(rB(ctx->opcode)); \ + gen_reset_fpstatus(); \ gen_op_f##op(); \ if (isfloat) { \ gen_op_frsp(); \ } \ gen_op_store_FT0_fpr(rD(ctx->opcode)); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_op_set_Rc1(); \ + gen_compute_fprf(set_fprf, Rc(ctx->opcode) != 0); \ } -#define GEN_FLOAT_AB(name, op2, inval) \ -_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0); \ -_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1); +#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ +_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type); \ +_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type); -#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat) \ -GEN_HANDLER(f##name, op1, op2, 0xFF, inval, PPC_FLOAT) \ +#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type) \ +GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type) \ { \ if (unlikely(!ctx->fpu_enabled)) { \ GEN_EXCP_NO_FP(ctx); \ return; \ } \ - gen_op_reset_scrfx(); \ gen_op_load_fpr_FT0(rA(ctx->opcode)); \ gen_op_load_fpr_FT1(rC(ctx->opcode)); \ + gen_reset_fpstatus(); \ gen_op_f##op(); \ if (isfloat) { \ gen_op_frsp(); \ } \ gen_op_store_FT0_fpr(rD(ctx->opcode)); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_op_set_Rc1(); \ + gen_compute_fprf(set_fprf, Rc(ctx->opcode) != 0); \ } -#define GEN_FLOAT_AC(name, op2, inval) \ -_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0); \ -_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1); +#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ +_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type); \ +_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type); -#define GEN_FLOAT_B(name, op2, op3, type) \ +#define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ GEN_HANDLER(f##name, 0x3F, op2, op3, 0x001F0000, type) \ { \ if (unlikely(!ctx->fpu_enabled)) { \ GEN_EXCP_NO_FP(ctx); \ return; \ } \ - gen_op_reset_scrfx(); \ gen_op_load_fpr_FT0(rB(ctx->opcode)); \ + gen_reset_fpstatus(); \ gen_op_f##name(); \ gen_op_store_FT0_fpr(rD(ctx->opcode)); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_op_set_Rc1(); \ + gen_compute_fprf(set_fprf, Rc(ctx->opcode) != 0); \ } -#define GEN_FLOAT_BS(name, op1, op2, type) \ +#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ GEN_HANDLER(f##name, op1, op2, 0xFF, 0x001F07C0, type) \ { \ if (unlikely(!ctx->fpu_enabled)) { \ GEN_EXCP_NO_FP(ctx); \ return; \ } \ - gen_op_reset_scrfx(); \ gen_op_load_fpr_FT0(rB(ctx->opcode)); \ + gen_reset_fpstatus(); \ gen_op_f##name(); \ gen_op_store_FT0_fpr(rD(ctx->opcode)); \ - if (unlikely(Rc(ctx->opcode) != 0)) \ - gen_op_set_Rc1(); \ + gen_compute_fprf(set_fprf, Rc(ctx->opcode) != 0); \ } /* fadd - fadds */ -GEN_FLOAT_AB(add, 0x15, 0x000007C0); +GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT); /* fdiv - fdivs */ -GEN_FLOAT_AB(div, 0x12, 0x000007C0); +GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT); /* fmul - fmuls */ -GEN_FLOAT_AC(mul, 0x19, 0x0000F800); +GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT); /* fre */ -GEN_FLOAT_BS(re, 0x3F, 0x18, PPC_FLOAT_EXT); +GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT); /* fres */ -GEN_FLOAT_BS(res, 0x3B, 0x18, PPC_FLOAT_FRES); +GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES); /* frsqrte */ -GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, PPC_FLOAT_FRSQRTE); +GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE); + +/* frsqrtes */ +static always_inline void gen_op_frsqrtes (void) +{ + gen_op_frsqrte(); + gen_op_frsp(); +} +GEN_FLOAT_BS(rsqrtes, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTES); /* fsel */ -_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0, PPC_FLOAT_FSEL); +_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0, 0, PPC_FLOAT_FSEL); /* fsub - fsubs */ -GEN_FLOAT_AB(sub, 0x14, 0x000007C0); +GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT); /* Optional: */ /* fsqrt */ GEN_HANDLER(fsqrt, 0x3F, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT) @@ -1782,12 +1820,11 @@ GEN_HANDLER(fsqrt, 0x3F, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT) GEN_EXCP_NO_FP(ctx); return; } - gen_op_reset_scrfx(); gen_op_load_fpr_FT0(rB(ctx->opcode)); + gen_reset_fpstatus(); gen_op_fsqrt(); gen_op_store_FT0_fpr(rD(ctx->opcode)); - if (unlikely(Rc(ctx->opcode) != 0)) - gen_op_set_Rc1(); + gen_compute_fprf(1, Rc(ctx->opcode) != 0); } GEN_HANDLER(fsqrts, 0x3B, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT) @@ -1796,49 +1833,48 @@ GEN_HANDLER(fsqrts, 0x3B, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT) GEN_EXCP_NO_FP(ctx); return; } - gen_op_reset_scrfx(); gen_op_load_fpr_FT0(rB(ctx->opcode)); + gen_reset_fpstatus(); gen_op_fsqrt(); gen_op_frsp(); gen_op_store_FT0_fpr(rD(ctx->opcode)); - if (unlikely(Rc(ctx->opcode) != 0)) - gen_op_set_Rc1(); + gen_compute_fprf(1, Rc(ctx->opcode) != 0); } /*** Floating-Point multiply-and-add ***/ /* fmadd - fmadds */ -GEN_FLOAT_ACB(madd, 0x1D, PPC_FLOAT); +GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT); /* fmsub - fmsubs */ -GEN_FLOAT_ACB(msub, 0x1C, PPC_FLOAT); +GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT); /* fnmadd - fnmadds */ -GEN_FLOAT_ACB(nmadd, 0x1F, PPC_FLOAT); +GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT); /* fnmsub - fnmsubs */ -GEN_FLOAT_ACB(nmsub, 0x1E, PPC_FLOAT); +GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT); /*** Floating-Point round & convert ***/ /* fctiw */ -GEN_FLOAT_B(ctiw, 0x0E, 0x00, PPC_FLOAT); +GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT); /* fctiwz */ -GEN_FLOAT_B(ctiwz, 0x0F, 0x00, PPC_FLOAT); +GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT); /* frsp */ -GEN_FLOAT_B(rsp, 0x0C, 0x00, PPC_FLOAT); +GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT); #if defined(TARGET_PPC64) /* fcfid */ -GEN_FLOAT_B(cfid, 0x0E, 0x1A, PPC_64B); +GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC_64B); /* fctid */ -GEN_FLOAT_B(ctid, 0x0E, 0x19, PPC_64B); +GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC_64B); /* fctidz */ -GEN_FLOAT_B(ctidz, 0x0F, 0x19, PPC_64B); +GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC_64B); #endif /* frin */ -GEN_FLOAT_B(rin, 0x08, 0x0C, PPC_FLOAT_EXT); +GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT); /* friz */ -GEN_FLOAT_B(riz, 0x08, 0x0D, PPC_FLOAT_EXT); +GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT); /* frip */ -GEN_FLOAT_B(rip, 0x08, 0x0E, PPC_FLOAT_EXT); +GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT); /* frim */ -GEN_FLOAT_B(rim, 0x08, 0x0F, PPC_FLOAT_EXT); +GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT); /*** Floating-Point compare ***/ /* fcmpo */ @@ -1848,11 +1884,12 @@ GEN_HANDLER(fcmpo, 0x3F, 0x00, 0x01, 0x00600001, PPC_FLOAT) GEN_EXCP_NO_FP(ctx); return; } - gen_op_reset_scrfx(); gen_op_load_fpr_FT0(rA(ctx->opcode)); gen_op_load_fpr_FT1(rB(ctx->opcode)); + gen_reset_fpstatus(); gen_op_fcmpo(); gen_op_store_T0_crf(crfD(ctx->opcode)); + gen_op_float_check_status(); } /* fcmpu */ @@ -1862,47 +1899,54 @@ GEN_HANDLER(fcmpu, 0x3F, 0x00, 0x00, 0x00600001, PPC_FLOAT) GEN_EXCP_NO_FP(ctx); return; } - gen_op_reset_scrfx(); gen_op_load_fpr_FT0(rA(ctx->opcode)); gen_op_load_fpr_FT1(rB(ctx->opcode)); + gen_reset_fpstatus(); gen_op_fcmpu(); gen_op_store_T0_crf(crfD(ctx->opcode)); + gen_op_float_check_status(); } /*** Floating-point move ***/ /* fabs */ -GEN_FLOAT_B(abs, 0x08, 0x08, PPC_FLOAT); +/* XXX: beware that fabs never checks for NaNs nor update FPSCR */ +GEN_FLOAT_B(abs, 0x08, 0x08, 0, PPC_FLOAT); /* fmr - fmr. */ +/* XXX: beware that fmr never checks for NaNs nor update FPSCR */ GEN_HANDLER(fmr, 0x3F, 0x08, 0x02, 0x001F0000, PPC_FLOAT) { if (unlikely(!ctx->fpu_enabled)) { GEN_EXCP_NO_FP(ctx); return; } - gen_op_reset_scrfx(); gen_op_load_fpr_FT0(rB(ctx->opcode)); gen_op_store_FT0_fpr(rD(ctx->opcode)); - if (unlikely(Rc(ctx->opcode) != 0)) - gen_op_set_Rc1(); + gen_compute_fprf(0, Rc(ctx->opcode) != 0); } /* fnabs */ -GEN_FLOAT_B(nabs, 0x08, 0x04, PPC_FLOAT); +/* XXX: beware that fnabs never checks for NaNs nor update FPSCR */ +GEN_FLOAT_B(nabs, 0x08, 0x04, 0, PPC_FLOAT); /* fneg */ -GEN_FLOAT_B(neg, 0x08, 0x01, PPC_FLOAT); +/* XXX: beware that fneg never checks for NaNs nor update FPSCR */ +GEN_FLOAT_B(neg, 0x08, 0x01, 0, PPC_FLOAT); /*** Floating-Point status & ctrl register ***/ /* mcrfs */ GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT) { + int bfa; + if (unlikely(!ctx->fpu_enabled)) { GEN_EXCP_NO_FP(ctx); return; } - gen_op_load_fpscr_T0(crfS(ctx->opcode)); + gen_optimize_fprf(); + bfa = 4 * (7 - crfS(ctx->opcode)); + gen_op_load_fpscr_T0(bfa); gen_op_store_T0_crf(crfD(ctx->opcode)); - gen_op_clear_fpscr(crfS(ctx->opcode)); + gen_op_fpscr_resetbit(~(0xF << bfa)); } /* mffs */ @@ -1912,10 +1956,11 @@ GEN_HANDLER(mffs, 0x3F, 0x07, 0x12, 0x001FF800, PPC_FLOAT) GEN_EXCP_NO_FP(ctx); return; } - gen_op_load_fpscr(); + gen_optimize_fprf(); + gen_reset_fpstatus(); + gen_op_load_fpscr_FT0(); gen_op_store_FT0_fpr(rD(ctx->opcode)); - if (unlikely(Rc(ctx->opcode) != 0)) - gen_op_set_Rc1(); + gen_compute_fprf(0, Rc(ctx->opcode) != 0); } /* mtfsb0 */ @@ -1927,12 +1972,15 @@ GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT) GEN_EXCP_NO_FP(ctx); return; } - crb = crbD(ctx->opcode) >> 2; - gen_op_load_fpscr_T0(crb); - gen_op_andi_T0(~(1 << (crbD(ctx->opcode) & 0x03))); - gen_op_store_T0_fpscr(crb); - if (unlikely(Rc(ctx->opcode) != 0)) - gen_op_set_Rc1(); + crb = 32 - (crbD(ctx->opcode) >> 2); + gen_optimize_fprf(); + gen_reset_fpstatus(); + if (likely(crb != 30 && crb != 29)) + gen_op_fpscr_resetbit(~(1 << crb)); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_op_load_fpcc(); + gen_op_set_Rc0(); + } } /* mtfsb1 */ @@ -1944,12 +1992,18 @@ GEN_HANDLER(mtfsb1, 0x3F, 0x06, 0x01, 0x001FF800, PPC_FLOAT) GEN_EXCP_NO_FP(ctx); return; } - crb = crbD(ctx->opcode) >> 2; - gen_op_load_fpscr_T0(crb); - gen_op_ori(1 << (crbD(ctx->opcode) & 0x03)); - gen_op_store_T0_fpscr(crb); - if (unlikely(Rc(ctx->opcode) != 0)) - gen_op_set_Rc1(); + crb = 32 - (crbD(ctx->opcode) >> 2); + gen_optimize_fprf(); + gen_reset_fpstatus(); + /* XXX: we pretend we can only do IEEE floating-point computations */ + if (likely(crb != FPSCR_FEX && crb != FPSCR_VX && crb != FPSCR_NI)) + gen_op_fpscr_setbit(crb); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_op_load_fpcc(); + gen_op_set_Rc0(); + } + /* We can raise a differed exception */ + gen_op_float_check_status(); } /* mtfsf */ @@ -1959,22 +2013,39 @@ GEN_HANDLER(mtfsf, 0x3F, 0x07, 0x16, 0x02010000, PPC_FLOAT) GEN_EXCP_NO_FP(ctx); return; } + gen_optimize_fprf(); gen_op_load_fpr_FT0(rB(ctx->opcode)); + gen_reset_fpstatus(); gen_op_store_fpscr(FM(ctx->opcode)); - if (unlikely(Rc(ctx->opcode) != 0)) - gen_op_set_Rc1(); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_op_load_fpcc(); + gen_op_set_Rc0(); + } + /* We can raise a differed exception */ + gen_op_float_check_status(); } /* mtfsfi */ GEN_HANDLER(mtfsfi, 0x3F, 0x06, 0x04, 0x006f0800, PPC_FLOAT) { + int bf, sh; + if (unlikely(!ctx->fpu_enabled)) { GEN_EXCP_NO_FP(ctx); return; } - gen_op_store_T0_fpscri(crbD(ctx->opcode) >> 2, FPIMM(ctx->opcode)); - if (unlikely(Rc(ctx->opcode) != 0)) - gen_op_set_Rc1(); + bf = crbD(ctx->opcode) >> 2; + sh = 7 - bf; + gen_optimize_fprf(); + gen_op_set_FT0(FPIMM(ctx->opcode) << (4 * sh)); + gen_reset_fpstatus(); + gen_op_store_fpscr(1 << sh); + if (unlikely(Rc(ctx->opcode) != 0)) { + gen_op_load_fpcc(); + gen_op_set_Rc0(); + } + /* We can raise a differed exception */ + gen_op_float_check_status(); } /*** Addressing modes ***/ @@ -6717,6 +6788,9 @@ static always_inline int gen_intermediate_code_internal (CPUState *env, gen_opc_ptr = gen_opc_buf; gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; gen_opparam_ptr = gen_opparam_buf; +#if defined(OPTIMIZE_FPRF_UPDATE) + gen_fprf_ptr = gen_fprf_buf; +#endif nb_gen_labels = 0; ctx.nip = pc_start; ctx.tb = tb;