commit a0cc18be5837339ad6da257a93597fc81a28c042 Author: Richard Henderson Date: Mon Dec 14 09:31:22 2009 -0800 target-alpha: Fix cvttq. The alpha fp-integer conversion instruction truncates instead of saturating like the generic IEEE version does. diff --git a/target-alpha/op_helper.c b/target-alpha/op_helper.c index b12c783..3bb0020 100644 --- a/target-alpha/op_helper.c +++ b/target-alpha/op_helper.c @@ -24,7 +24,7 @@ /*****************************************************************************/ /* Exceptions processing helpers */ -void helper_excp (int excp, int error) +void QEMU_NORETURN helper_excp (int excp, int error) { env->exception_index = excp; env->error_code = error; @@ -1166,15 +1166,112 @@ uint64_t helper_cvtqs (uint64_t a, uint32_t quals) uint64_t helper_cvttq (uint64_t a, uint32_t quals) { - float64 fa; - uint64_t ret; - uint32_t token; - - fa = t_to_float64(a); + uint64_t ret, frac; + uint32_t token, exp, sign, exc = 0; token = begin_fp(quals); - float64_input(quals, fa); - ret = float64_to_int64(fa, &FP_STATUS); + + /* Alpha integer conversion does not saturate, as the generic routine + does. Instead it supplies a truncated result. This fact is relied + upon by GCC in that without overflow enabled we can get unsigned + conversion for free with the same instruction. */ + + sign = (a >> 63); + exp = (uint32_t)(a >> 52) & 0x7ff; + frac = a & 0xfffffffffffffull; + + if (exp == 0) { + ret = 0; + if (frac != 0) { + /* ??? If DNZ set, map to zero without trapping. */ + /* ??? Figure out what kind of exception signal to send. */ + if (!(quals & 0x400)) + helper_excp(EXCP_ARITH, 0); + goto do_underflow; + } + } else if (exp == 0x7ff) { + /* In keeping with the truncation result, both infinity and NaN + give result of zero. See Table B-2 in the Alpha Architecture + Handbook. */ + ret = 0; + exc = float_flag_invalid; + + /* Without /s qualifier, both Inf and NaN trap. SNaN always traps. */ + if (!(quals & 0x400) || (frac & 0x4000000000000ull)) + helper_excp(EXCP_ARITH, 0); + } else { + int32_t shift; + + /* Restore implicit bit. */ + frac |= 0x10000000000000ull; + + shift = exp - 1023 - 52; + if (shift > 0) { + /* In this case the number is so large that we must shift + the fraction left. There is no rounding to do, but we + must still set inexact for overflow. */ + if (shift < 63) { + ret = frac << shift; + if ((ret >> shift) != frac) + exc = float_flag_inexact; + } else { + exc = float_flag_inexact; + ret = 0; + } + } else if (shift == 0) { + /* The exponent is exactly right for the 52-bit fraction. */ + ret = frac; + } else { + uint64_t round; + + /* In this case the number is smaller than the fraction as + represented by the 52 bit number. Here we must think + about rounding the result. Handle this by shifting the + fractional part of the number into the high bits of ROUND. + This will let us efficiently handle round-to-nearest. */ + shift = -shift; + if (shift < 63) { + ret = frac >> shift; + round = frac << (64 - shift); + } else { + do_underflow: + /* The exponent is so small we shift out everything. */ + ret = 0; + round = 1; + } + + if (round) { + exc = float_flag_inexact; + switch (FP_STATUS.float_rounding_mode) { + case float_round_nearest_even: + if (round == (1ull << 63)) { + /* The remaining fraction is exactly 0.5; + round to even. */ + ret += (ret & 1); + } else if (round > (1ull << 63)) { + ret += 1; + } + break; + case float_round_to_zero: + break; + case float_round_up: + if (!sign) + ret += 1; + break; + case float_round_down: + if (sign) + ret += 1; + break; + } + } + } + + if (sign) + ret = -ret; + } + + if (exc) + float_raise(exc, &FP_STATUS); end_fp(quals, token); return ret;