From: Matt Turner <[email protected]>
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. Pass
soft_to_fpcr_exc whether the delivered result is normal, and drop
underflow when it is. Only the S and T format add, sub, mul, div and
cvtts helpers can underflow; the rest pass false.
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. That
check is for CONFIG_USER_ONLY only: in system mode gen_ieee_input raises
the trap before the helper runs, and with DNZ set the operand is flushed
rather than used.
Signed-off-by: Matt Turner <[email protected]>
Reviewed-by: Richard Henderson <[email protected]>
Message-ID: <[email protected]>
Signed-off-by: Philippe Mathieu-Daudé <[email protected]>
---
target/alpha/fpu_helper.c | 60 ++++++++++++++++++++++++++-------------
1 file changed, 40 insertions(+), 20 deletions(-)
diff --git a/target/alpha/fpu_helper.c b/target/alpha/fpu_helper.c
index 643ce641e83..e91e907c215 100644
--- a/target/alpha/fpu_helper.c
+++ b/target/alpha/fpu_helper.c
@@ -38,7 +38,11 @@ void helper_setflushzero(CPUAlphaState *env, uint32_t val)
#define CONVERT_BIT(X, SRC, DST) \
(SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
-static uint32_t soft_to_fpcr_exc(CPUAlphaState *env)
+/*
+ * NORMAL is whether the delivered result is normal. Operations that
+ * cannot underflow pass false.
+ */
+static uint32_t soft_to_fpcr_exc(CPUAlphaState *env, bool normal)
{
FloatExceptionFlags exc = get_float_exception_flags(&FP_STATUS);
uint32_t ret = 0;
@@ -48,8 +52,24 @@ static uint32_t soft_to_fpcr_exc(CPUAlphaState *env)
ret |= CONVERT_BIT(exc, float_flag_invalid, FPCR_INV);
ret |= CONVERT_BIT(exc, float_flag_divbyzero, FPCR_DZE);
ret |= CONVERT_BIT(exc, float_flag_overflow, FPCR_OVF);
- ret |= CONVERT_BIT(exc, float_flag_underflow, FPCR_UNF);
ret |= CONVERT_BIT(exc, float_flag_inexact, FPCR_INE);
+
+ /*
+ * 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. In user mode, a denormal operand means the
+ * result is the one the kernel's software completion would compute,
+ * and that does raise underflow.
+ */
+ if (exc & float_flag_underflow) {
+ bool tiny = !normal;
+#ifdef CONFIG_USER_ONLY
+ tiny |= exc & float_flag_input_denormal_used;
+#endif
+ if (tiny) {
+ ret |= FPCR_UNF;
+ }
+ }
}
return ret;
@@ -227,7 +247,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(env, float32_is_normal(fr));
return float32_to_s(fr);
}
@@ -239,7 +259,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(env, float32_is_normal(fr));
return float32_to_s(fr);
}
@@ -251,7 +271,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(env, float32_is_normal(fr));
return float32_to_s(fr);
}
@@ -263,7 +283,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(env, float32_is_normal(fr));
return float32_to_s(fr);
}
@@ -274,7 +294,7 @@ uint64_t helper_sqrts(CPUAlphaState *env, uint64_t a)
fa = s_to_float32(a);
fr = float32_sqrt(fa, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc(env, false);
return float32_to_s(fr);
}
@@ -304,7 +324,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(env, float64_is_normal(fr));
return float64_to_t(fr);
}
@@ -316,7 +336,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(env, float64_is_normal(fr));
return float64_to_t(fr);
}
@@ -328,7 +348,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(env, float64_is_normal(fr));
return float64_to_t(fr);
}
@@ -340,7 +360,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(env, float64_is_normal(fr));
return float64_to_t(fr);
}
@@ -351,7 +371,7 @@ uint64_t helper_sqrtt(CPUAlphaState *env, uint64_t a)
fa = t_to_float64(a);
fr = float64_sqrt(fa, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc(env, false);
return float64_to_t(fr);
}
@@ -368,7 +388,7 @@ uint64_t helper_cmptun(CPUAlphaState *env, uint64_t a,
uint64_t b)
if (float64_unordered_quiet(fa, fb, &FP_STATUS)) {
ret = 0x4000000000000000ULL;
}
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc(env, false);
return ret;
}
@@ -384,7 +404,7 @@ uint64_t helper_cmpteq(CPUAlphaState *env, uint64_t a,
uint64_t b)
if (float64_eq_quiet(fa, fb, &FP_STATUS)) {
ret = 0x4000000000000000ULL;
}
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc(env, false);
return ret;
}
@@ -400,7 +420,7 @@ uint64_t helper_cmptle(CPUAlphaState *env, uint64_t a,
uint64_t b)
if (float64_le(fa, fb, &FP_STATUS)) {
ret = 0x4000000000000000ULL;
}
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc(env, false);
return ret;
}
@@ -416,7 +436,7 @@ uint64_t helper_cmptlt(CPUAlphaState *env, uint64_t a,
uint64_t b)
if (float64_lt(fa, fb, &FP_STATUS)) {
ret = 0x4000000000000000ULL;
}
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc(env, false);
return ret;
}
@@ -429,7 +449,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(env, float32_is_normal(fr));
return float32_to_s(fr);
}
@@ -441,7 +461,7 @@ uint64_t helper_cvtst(CPUAlphaState *env, uint64_t a)
fa = s_to_float32(a);
fr = float32_to_float64(fa, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc(env, false);
return float64_to_t(fr);
}
@@ -449,7 +469,7 @@ uint64_t helper_cvtst(CPUAlphaState *env, uint64_t a)
uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a)
{
float32 fr = int64_to_float32(a, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc(env, false);
return float32_to_s(fr);
}
@@ -504,7 +524,7 @@ uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a)
uint64_t helper_cvtqt(CPUAlphaState *env, uint64_t a)
{
float64 fr = int64_to_float64(a, &FP_STATUS);
- env->error_code = soft_to_fpcr_exc(env);
+ env->error_code = soft_to_fpcr_exc(env, false);
return float64_to_t(fr);
}
--
2.55.0