target/i386: implement special cases for fxtract

The implementation of the fxtract instruction treats all nonzero
operands as normal numbers, so yielding incorrect results for invalid
formats, infinities, NaNs and subnormal and pseudo-denormal operands.
Implement appropriate handling of all those cases.

Signed-off-by: Joseph Myers <joseph@codesourcery.com>
Acked-by: Alex Bennée <alex.bennee@linaro.org>
Message-Id: <alpine.DEB.2.21.2005070042360.18350@digraph.polyomino.org.uk>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Joseph Myers 2020-05-07 00:43:30 +00:00 committed by Paolo Bonzini
parent 2b151297e4
commit c415f2c582
2 changed files with 144 additions and 1 deletions

View File

@ -767,10 +767,33 @@ void helper_fxtract(CPUX86State *env)
&env->fp_status);
fpush(env);
ST0 = temp.d;
} else if (floatx80_invalid_encoding(ST0)) {
float_raise(float_flag_invalid, &env->fp_status);
ST0 = floatx80_default_nan(&env->fp_status);
fpush(env);
ST0 = ST1;
} else if (floatx80_is_any_nan(ST0)) {
if (floatx80_is_signaling_nan(ST0, &env->fp_status)) {
float_raise(float_flag_invalid, &env->fp_status);
ST0 = floatx80_silence_nan(ST0, &env->fp_status);
}
fpush(env);
ST0 = ST1;
} else if (floatx80_is_infinity(ST0)) {
fpush(env);
ST0 = ST1;
ST1 = floatx80_infinity;
} else {
int expdif;
expdif = EXPD(temp) - EXPBIAS;
if (EXPD(temp) == 0) {
int shift = clz64(temp.l.lower);
temp.l.lower <<= shift;
expdif = 1 - EXPBIAS - shift;
float_raise(float_flag_input_denormal, &env->fp_status);
} else {
expdif = EXPD(temp) - EXPBIAS;
}
/* DP exponent bias */
ST0 = int32_to_floatx80(expdif, &env->fp_status);
fpush(env);

View File

@ -0,0 +1,120 @@
/* Test fxtract instruction. */
#include <stdint.h>
#include <stdio.h>
union u {
struct { uint64_t sig; uint16_t sign_exp; } s;
long double ld;
};
volatile union u ld_pseudo_m16382 = { .s = { UINT64_C(1) << 63, 0 } };
volatile union u ld_invalid_1 = { .s = { 1, 1234 } };
volatile union u ld_invalid_2 = { .s = { 0, 1234 } };
volatile union u ld_invalid_3 = { .s = { 0, 0x7fff } };
volatile union u ld_invalid_4 = { .s = { (UINT64_C(1) << 63) - 1, 0x7fff } };
volatile long double ld_sig, ld_exp;
int isnan_ld(long double x)
{
union u tmp = { .ld = x };
return ((tmp.s.sign_exp & 0x7fff) == 0x7fff &&
(tmp.s.sig >> 63) != 0 &&
(tmp.s.sig << 1) != 0);
}
int issignaling_ld(long double x)
{
union u tmp = { .ld = x };
return isnan_ld(x) && (tmp.s.sig & UINT64_C(0x4000000000000000)) == 0;
}
int main(void)
{
int ret = 0;
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) : "0" (2.5L));
if (ld_sig != 1.25L || ld_exp != 1.0L) {
printf("FAIL: fxtract 2.5\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) : "0" (0.0L));
if (ld_sig != 0.0L || __builtin_copysignl(1.0L, ld_sig) != 1.0L ||
ld_exp != -__builtin_infl()) {
printf("FAIL: fxtract 0.0\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) : "0" (-0.0L));
if (ld_sig != -0.0L || __builtin_copysignl(1.0L, ld_sig) != -1.0L ||
ld_exp != -__builtin_infl()) {
printf("FAIL: fxtract -0.0\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) :
"0" (__builtin_infl()));
if (ld_sig != __builtin_infl() || ld_exp != __builtin_infl()) {
printf("FAIL: fxtract inf\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) :
"0" (-__builtin_infl()));
if (ld_sig != -__builtin_infl() || ld_exp != __builtin_infl()) {
printf("FAIL: fxtract -inf\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) :
"0" (__builtin_nanl("")));
if (!isnan_ld(ld_sig) || issignaling_ld(ld_sig) ||
!isnan_ld(ld_exp) || issignaling_ld(ld_exp)) {
printf("FAIL: fxtract qnan\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) :
"0" (__builtin_nansl("")));
if (!isnan_ld(ld_sig) || issignaling_ld(ld_sig) ||
!isnan_ld(ld_exp) || issignaling_ld(ld_exp)) {
printf("FAIL: fxtract snan\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) :
"0" (0x1p-16445L));
if (ld_sig != 1.0L || ld_exp != -16445.0L) {
printf("FAIL: fxtract subnormal\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) :
"0" (ld_pseudo_m16382.ld));
if (ld_sig != 1.0L || ld_exp != -16382.0L) {
printf("FAIL: fxtract pseudo\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) :
"0" (ld_invalid_1.ld));
if (!isnan_ld(ld_sig) || issignaling_ld(ld_sig) ||
!isnan_ld(ld_exp) || issignaling_ld(ld_exp)) {
printf("FAIL: fxtract invalid 1\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) :
"0" (ld_invalid_2.ld));
if (!isnan_ld(ld_sig) || issignaling_ld(ld_sig) ||
!isnan_ld(ld_exp) || issignaling_ld(ld_exp)) {
printf("FAIL: fxtract invalid 2\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) :
"0" (ld_invalid_3.ld));
if (!isnan_ld(ld_sig) || issignaling_ld(ld_sig) ||
!isnan_ld(ld_exp) || issignaling_ld(ld_exp)) {
printf("FAIL: fxtract invalid 3\n");
ret = 1;
}
__asm__ volatile ("fxtract" : "=t" (ld_sig), "=u" (ld_exp) :
"0" (ld_invalid_4.ld));
if (!isnan_ld(ld_sig) || issignaling_ld(ld_sig) ||
!isnan_ld(ld_exp) || issignaling_ld(ld_exp)) {
printf("FAIL: fxtract invalid 4\n");
ret = 1;
}
return ret;
}