summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--target/ppc/fpu_helper.c62
1 files changed, 37 insertions, 25 deletions
diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c
index 44f3fed17d..5af5241ab0 100644
--- a/target/ppc/fpu_helper.c
+++ b/target/ppc/fpu_helper.c
@@ -879,18 +879,27 @@ float64 helper_fsqrt(CPUPPCState *env, float64 arg)
 }
 
 /* fre - fre. */
-uint64_t helper_fre(CPUPPCState *env, uint64_t arg)
+float64 helper_fre(CPUPPCState *env, float64 arg)
 {
-    CPU_DoubleU farg;
-
-    farg.ll = arg;
+    /* "Estimate" the reciprocal with actual division.  */
+    float64 ret = float64_div(float64_one, arg, &env->fp_status);
+    int status = get_float_exception_flags(&env->fp_status);
 
-    if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) {
-        /* sNaN reciprocal */
-        float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
+    if (unlikely(status)) {
+        if (status & float_flag_invalid) {
+            if (float64_is_signaling_nan(arg, &env->fp_status)) {
+                /* sNaN reciprocal */
+                float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
+            }
+        }
+        if (status & float_flag_divbyzero) {
+            float_zero_divide_excp(env, GETPC());
+            /* For FPSCR.ZE == 0, the result is 1/2.  */
+            ret = float64_set_sign(float64_half, float64_is_neg(arg));
+        }
     }
-    farg.d = float64_div(float64_one, farg.d, &env->fp_status);
-    return farg.d;
+
+    return ret;
 }
 
 /* fres - fres. */
@@ -913,27 +922,30 @@ uint64_t helper_fres(CPUPPCState *env, uint64_t arg)
 }
 
 /* frsqrte  - frsqrte. */
-uint64_t helper_frsqrte(CPUPPCState *env, uint64_t arg)
+float64 helper_frsqrte(CPUPPCState *env, float64 arg)
 {
-    CPU_DoubleU farg;
-
-    farg.ll = arg;
+    /* "Estimate" the reciprocal with actual division.  */
+    float64 rets = float64_sqrt(arg, &env->fp_status);
+    float64 retd = float64_div(float64_one, rets, &env->fp_status);
+    int status = get_float_exception_flags(&env->fp_status);
 
-    if (unlikely(float64_is_any_nan(farg.d))) {
-        if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) {
-            /* sNaN reciprocal square root */
-            float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
-            farg.ll = float64_snan_to_qnan(farg.ll);
+    if (unlikely(status)) {
+        if (status & float_flag_invalid) {
+            if (float64_is_signaling_nan(arg, &env->fp_status)) {
+                /* sNaN reciprocal */
+                float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1);
+            } else {
+                /* Square root of a negative nonzero number */
+                float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1);
+            }
+        }
+        if (status & float_flag_divbyzero) {
+            /* Reciprocal of (square root of) zero.  */
+            float_zero_divide_excp(env, GETPC());
         }
-    } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) {
-        /* Reciprocal square root of a negative nonzero number */
-        farg.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1);
-    } else {
-        farg.d = float64_sqrt(farg.d, &env->fp_status);
-        farg.d = float64_div(float64_one, farg.d, &env->fp_status);
     }
 
-    return farg.ll;
+    return retd;
 }
 
 /* fsel - fsel. */