target-alpha: Raise EXC_M_INV properly for fp inputs

Ignore DNZ if software completion isn't used.  Raise INV for
denormals in system mode so the OS completion handler sees them.

Reported-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Richard Henderson <rth@twiddle.net>
This commit is contained in:
Richard Henderson 2014-07-08 10:11:06 -07:00
parent ed0851380c
commit b99e80694c
3 changed files with 30 additions and 10 deletions

View File

@ -104,16 +104,14 @@ void helper_ieee_input(CPUAlphaState *env, uint64_t val)
uint64_t frac = val & 0xfffffffffffffull;
if (exp == 0) {
/* Denormals without DNZ set raise an exception. */
if (frac != 0 && !env->fp_status.flush_inputs_to_zero) {
arith_excp(env, GETPC(), EXC_M_UNF, 0);
/* Denormals without /S raise an exception. */
if (frac != 0) {
arith_excp(env, GETPC(), EXC_M_INV, 0);
}
} else if (exp == 0x7ff) {
/* Infinity or NaN. */
/* ??? I'm not sure these exception bit flags are correct. I do
know that the Linux kernel, at least, doesn't rely on them and
just emulates the insn to figure out what exception to use. */
arith_excp(env, GETPC(), frac ? EXC_M_INV : EXC_M_FOV, 0);
env->fpcr |= FPCR_INV;
arith_excp(env, GETPC(), EXC_M_INV, 0);
}
}
@ -124,16 +122,30 @@ void helper_ieee_input_cmp(CPUAlphaState *env, uint64_t val)
uint64_t frac = val & 0xfffffffffffffull;
if (exp == 0) {
/* Denormals without DNZ set raise an exception. */
if (frac != 0 && !env->fp_status.flush_inputs_to_zero) {
arith_excp(env, GETPC(), EXC_M_UNF, 0);
/* Denormals without /S raise an exception. */
if (frac != 0) {
arith_excp(env, GETPC(), EXC_M_INV, 0);
}
} else if (exp == 0x7ff && frac) {
/* NaN. */
env->fpcr |= FPCR_INV;
arith_excp(env, GETPC(), EXC_M_INV, 0);
}
}
/* Input handing with software completion. Trap for denorms, unless DNZ
is set. If we try to support DNOD (which none of the produced hardware
did, AFAICS), we'll need to suppress the trap when FPCR.DNOD is set;
then the code downstream of that will need to cope with denorms sans
flush_input_to_zero. Most of it should work sanely, but there's
nothing to compare with. */
void helper_ieee_input_s(CPUAlphaState *env, uint64_t val)
{
if (unlikely(2 * val - 1 < 0x1fffffffffffffull)
&& !env->fp_status.flush_inputs_to_zero) {
arith_excp(env, GETPC(), EXC_M_INV | EXC_M_SWC, 0);
}
}
/* S floating (single) */

View File

@ -86,6 +86,7 @@ DEF_HELPER_FLAGS_3(fp_exc_raise_s, TCG_CALL_NO_WG, void, env, i32, i32)
DEF_HELPER_FLAGS_2(ieee_input, TCG_CALL_NO_WG, void, env, i64)
DEF_HELPER_FLAGS_2(ieee_input_cmp, TCG_CALL_NO_WG, void, env, i64)
DEF_HELPER_FLAGS_2(ieee_input_s, TCG_CALL_NO_WG, void, env, i64)
DEF_HELPER_FLAGS_2(cvtql_v_input, TCG_CALL_NO_WG, void, env, i64)
#if !defined (CONFIG_USER_ONLY)

View File

@ -658,6 +658,13 @@ static TCGv gen_ieee_input(DisasContext *ctx, int reg, int fn11, int is_cmp)
} else {
gen_helper_ieee_input(cpu_env, val);
}
} else {
#ifndef CONFIG_USER_ONLY
/* In system mode, raise exceptions for denormals like real
hardware. In user mode, proceed as if the OS completion
handler is handling the denormal as per spec. */
gen_helper_ieee_input_s(cpu_env, val);
#endif
}
}
return val;