[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH] Bug in target-i386/helper.c:helper_fxam_ST0
From: |
Julian Seward |
Subject: |
[Qemu-devel] [PATCH] Bug in target-i386/helper.c:helper_fxam_ST0 |
Date: |
Mon, 19 Jun 2006 13:25:22 +0100 |
User-agent: |
KMail/1.9.1 |
I've been doing some instruction set testing on i386-softmmu,
with the aim of seeing if I can find any anomalies which might
be the cause the of Win2K SP4 installation failure.
helper_fxam_ST0 doesn't correctly distinguish infinities from
nans, and thereby causes programs that use the x86 'fxam'
instruction to occasionally produce incorrect results. That
instruction is quite often used as part of transcendentals, for
example pow, exp, log.
On a Linux guest, it for example causes the libc call pow(0.6, inf)
to produce inf when it should produce zero, and causes about 20
cases in the FP correctness suite I'm using to fail.
The test case below shows the problem. It should produce
0x4000: 0.000000
0x4200: -0.000000
0x0500: inf
0x0700: -inf
0x0300: nan
0x0100: nan
0x0400: 0.000000
0x0600: -0.000000
0x0400: 1.230000
0x0600: -1.230000
but instead produces (omitting the correct cases)
0x0100: inf
0x0300: -inf
What's strange is the logic in helper_fxam_ST0 looks correct.
The distinguish-nans-from-infinities part is
if (expdif == MAXEXPD) {
if (MANTD(temp) == 0)
env->fpus |= 0x500 /*Infinity*/;
else
env->fpus |= 0x100 /*NaN*/;
}
I suspect the check is correct for 52-bit mantissas (64-bit floats)
but not for 64-bit mantissas (80-bit floats), as per these notes:
/* 80 and 64-bit floating point formats:
80-bit:
S 0 0-------0 zero
S 0 0X------X denormals
S 1-7FFE 1X------X normals (all normals have leading 1)
S 7FFF 10------0 infinity
S 7FFF 10X-----X snan
S 7FFF 11X-----X qnan
S is the sign bit. For runs X----X, at least one of the Xs must be
nonzero. Exponent is 15 bits, fractional part is 63 bits, and
there is an explicitly represented leading 1, and a sign bit,
giving 80 in total.
64-bit avoids the confusion of an explicitly represented leading 1
and so is simpler:
S 0 0------0 zero
S 0 X------X denormals
S 1-7FE any normals
S 7FF 0------0 infinity
S 7FF 0X-----X snan
S 7FF 1X-----X qnan
Exponent is 11 bits, fractional part is 52 bits, and there is a
sign bit, giving 64 in total.
*/
For 52-bit mantissas, the mantissa zero-vs-nonzero check is correct.
But for 64-bit mantissas, the check needs to be
if (MANTD(temp) == 0x8000000000000000ULL)
and indeed setting it to that makes the test program run correctly.
Patch and testcase follow.
I'm still seeing cases where x87-based computation on qemu winds
up with a NaN when it shouldn't. I think that's a separate problem.
Will investigate.
J
-------------------------------------------------------------------------
Index: target-i386/helper.c
===================================================================
RCS file: /sources/qemu/qemu/target-i386/helper.c,v
retrieving revision 1.65
diff -r1.65 helper.c
2952a2953,2955
> # ifdef USE_X86LDOUBLE
> if (MANTD(temp) == 0x8000000000000000ULL)
> # else
2953a2957
> # endif
-------------------------------------------------------------------------
#include <stdio.h>
#include <math.h>
/* FPU flag masks */
#define X86G_FC_SHIFT_C3 14
#define X86G_FC_SHIFT_C2 10
#define X86G_FC_SHIFT_C1 9
#define X86G_FC_SHIFT_C0 8
#define X86G_FC_MASK_C3 (1 << X86G_FC_SHIFT_C3)
#define X86G_FC_MASK_C2 (1 << X86G_FC_SHIFT_C2)
#define X86G_FC_MASK_C1 (1 << X86G_FC_SHIFT_C1)
#define X86G_FC_MASK_C0 (1 << X86G_FC_SHIFT_C0)
#define MASK_C3210 (X86G_FC_MASK_C3 | X86G_FC_MASK_C2 | X86G_FC_MASK_C1 |
X86G_FC_MASK_C0)
double d;
int i;
extern void do_fxam ( void );
asm(
"\n"
"do_fxam:\n"
"\txorl %eax,%eax\n"
"\tfldl d\n"
"\tfxam\n"
"\tfnstsw %ax\n"
"\tffree %st(0)\n"
"\tmovl %eax, i\n"
"\tret\n"
);
double inf ( void ) { return 1.0 / 0.0; }
double nAn ( void ) { return 0.0 / 0.0; }
double den ( void ) { return 9.1e-220 / 1e100; }
double nor ( void ) { return 1.23; }
/* Try positive and negative variants of: zero, infinity,
nAn, denorm and normal */
int main ( void )
{
d = 0.0; do_fxam(); printf("0x%04x: %f\n", i & MASK_C3210, d );
d = -0.0; do_fxam(); printf("0x%04x: %f\n", i & MASK_C3210, d );
d = inf(); do_fxam(); printf("0x%04x: %f\n", i & MASK_C3210, d );
d = -inf(); do_fxam(); printf("0x%04x: %f\n", i & MASK_C3210, d );
d = nAn(); do_fxam(); printf("0x%04x: %f\n", i & MASK_C3210, d );
d = -nAn(); do_fxam(); printf("0x%04x: %f\n", i & MASK_C3210, d );
d = den(); do_fxam(); printf("0x%04x: %f\n", i & MASK_C3210, d );
d = -den(); do_fxam(); printf("0x%04x: %f\n", i & MASK_C3210, d );
d = nor(); do_fxam(); printf("0x%04x: %f\n", i & MASK_C3210, d );
d = -nor(); do_fxam(); printf("0x%04x: %f\n", i & MASK_C3210, d );
return 0;
}
- [Qemu-devel] [PATCH] Bug in target-i386/helper.c:helper_fxam_ST0,
Julian Seward <=