Alpha raises underflow only if the result it delivers is tiny.
softfloat implements IEEE 754 tininess after rounding, which rounds as
if the exponent range were unbounded. The two disagree about a result
that rounds up to the smallest normal:
0x4p-1024 / 0x1.0000000000001p+0 in FE_UPWARD = 0x4p-1024
The binade below DBL_MIN has a finer spacing than the denormals, so
with an unbounded exponent the quotient rounds to a value below
DBL_MIN and softfloat raises underflow. Alpha does not. glibc marks
math/test-float32x-float64-div as XFAIL on Alpha because of this
(glibc commit a32db99a38), and under qemu that test unexpectedly
passes.
Every result that is tiny by Alpha's rule is also tiny by the IEEE
one, so there is no need for another tininess mode in softfloat. Drop
underflow in the S and T format add, sub, mul, div and cvtts helpers
when the delivered result is normal.
An insn with a denormal operand is different. Unless DNZ is set it
traps for software completion and the kernel computes the result, and
the kernel's emulation raises underflow in the same situation:
0x3.ffffffffffffcp-1024 / 0x1.fffffffffffffp-1 in FE_UPWARD
= 0x4p-1024, FPCR_UNF set
That result is tiny whether tininess is detected before rounding, as
math-emu does, or after rounding, as softfloat does. linux-user
computes these results in the helper instead of trapping, so keep
softfloat's underflow when float_flag_input_denormal_used is set. In
system mode gen_ieee_input raises the trap before the helper runs.
Signed-off-by: Matt Turner <[email protected]>
---
target/alpha/fpu_helper.c | 36 +++++++++++++++++++++++++++---------
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/target/alpha/fpu_helper.c b/target/alpha/fpu_helper.c
index 643ce641e8..d902cdd9dc 100644
--- a/target/alpha/fpu_helper.c
+++ b/target/alpha/fpu_helper.c
@@ -55,6 +55,24 @@ static uint32_t soft_to_fpcr_exc(CPUAlphaState *env)
return ret;
}
+/*
+ * Alpha raises no underflow when the delivered result is normal.
+ * softfloat does when that result was rounded up to the smallest
+ * normal, so drop it, except when a denormal operand means the kernel
+ * computed the result.
+ */
+static uint32_t soft_to_fpcr_exc_tiny(CPUAlphaState *env, bool normal)
+{
+ bool swc = get_float_exception_flags(&FP_STATUS)
+ & float_flag_input_denormal_used;
+ uint32_t ret = soft_to_fpcr_exc(env);
+
+ if (unlikely(ret & FPCR_UNF) && normal && !swc) {
+ ret &= ~FPCR_UNF;
+ }
+ return ret;
+}
+
static void fp_exc_raise1(CPUAlphaState *env, uintptr_t retaddr,
uint32_t exc, uint32_t regno, uint32_t hw_exc)
{
@@ -227,7 +245,7 @@ uint64_t helper_adds(CPUAlphaState *env, uint64_t a,
uint64_t b)
fa = s_to_float32(a);
fb = s_to_float32(b);
fr = float32_add(fa, fb, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc_tiny(env, float32_is_normal(fr));
return float32_to_s(fr);
}
@@ -239,7 +257,7 @@ uint64_t helper_subs(CPUAlphaState *env, uint64_t a,
uint64_t b)
fa = s_to_float32(a);
fb = s_to_float32(b);
fr = float32_sub(fa, fb, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc_tiny(env, float32_is_normal(fr));
return float32_to_s(fr);
}
@@ -251,7 +269,7 @@ uint64_t helper_muls(CPUAlphaState *env, uint64_t a,
uint64_t b)
fa = s_to_float32(a);
fb = s_to_float32(b);
fr = float32_mul(fa, fb, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc_tiny(env, float32_is_normal(fr));
return float32_to_s(fr);
}
@@ -263,7 +281,7 @@ uint64_t helper_alpha_divs(CPUAlphaState *env, uint64_t a,
uint64_t b)
fa = s_to_float32(a);
fb = s_to_float32(b);
fr = float32_div(fa, fb, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc_tiny(env, float32_is_normal(fr));
return float32_to_s(fr);
}
@@ -304,7 +322,7 @@ uint64_t helper_addt(CPUAlphaState *env, uint64_t a,
uint64_t b)
fa = t_to_float64(a);
fb = t_to_float64(b);
fr = float64_add(fa, fb, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc_tiny(env, float64_is_normal(fr));
return float64_to_t(fr);
}
@@ -316,7 +334,7 @@ uint64_t helper_subt(CPUAlphaState *env, uint64_t a,
uint64_t b)
fa = t_to_float64(a);
fb = t_to_float64(b);
fr = float64_sub(fa, fb, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc_tiny(env, float64_is_normal(fr));
return float64_to_t(fr);
}
@@ -328,7 +346,7 @@ uint64_t helper_mult(CPUAlphaState *env, uint64_t a,
uint64_t b)
fa = t_to_float64(a);
fb = t_to_float64(b);
fr = float64_mul(fa, fb, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc_tiny(env, float64_is_normal(fr));
return float64_to_t(fr);
}
@@ -340,7 +358,7 @@ uint64_t helper_divt(CPUAlphaState *env, uint64_t a,
uint64_t b)
fa = t_to_float64(a);
fb = t_to_float64(b);
fr = float64_div(fa, fb, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc_tiny(env, float64_is_normal(fr));
return float64_to_t(fr);
}
@@ -429,7 +447,7 @@ uint64_t helper_cvtts(CPUAlphaState *env, uint64_t a)
fa = t_to_float64(a);
fr = float64_to_float32(fa, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc_tiny(env, float32_is_normal(fr));
return float32_to_s(fr);
}
--
2.55.0