aboutsummaryrefslogtreecommitdiff
path: root/target-ppc/op_helper.c
diff options
context:
space:
mode:
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>2007-10-27 17:54:30 +0000
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>2007-10-27 17:54:30 +0000
commit7c58044c0ab79f11604f71aa04b4691baacef886 (patch)
treeb8a4f5b7e4ab2ada25ced61c0bd05a5d6a49e3f2 /target-ppc/op_helper.c
parenta32ff1ad95c4f09420c4b5e19dfd86ded0d2bc79 (diff)
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
Diffstat (limited to 'target-ppc/op_helper.c')
-rw-r--r--target-ppc/op_helper.c919
1 files changed, 740 insertions, 179 deletions
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;
-
- 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);
+ /* 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);
#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;
-
- 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);
+ /* 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);
#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;
-
- 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);
+ /* 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);
#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;
-
- 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);
+ /* 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);
#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)