qemu-devel
[Top][All Lists]
Advanced

[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;
}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]