qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH,RFC,RFT] arm floating point breakage


From: Lennert Buytenhek
Subject: [Qemu-devel] [PATCH,RFC,RFT] arm floating point breakage
Date: Fri, 31 Dec 2004 14:31:32 +0100
User-agent: Mutt/1.4.1i

Hi,

Currently, floating point emulation for ARM targets only works if both
host and target are little-endian.  This is due to a number of things:

1. When copying floats and doubles back and forth between nwfpe and
   'userspace', no byteswapping is done.  This manifests itself on an
   x86 host doing big-endian ARM emulation as '(float)123.0 + (float)123.0'
   ending up with the value '(float)12636.429688'

2. NWFPE deals with floats as 'host' u32's and doubles as 'host' u64's,
   but it unconditionally swaps the halves of each double when copying
   them back and forth between userspace, which is only needed for
   little-endian host platforms.

   Explanation: on the ARM architecture, in the FPA float model, doubles
   are stored with their sub-u32's in native byte order (big-endian or
   little-endian), but the most significant u32 is always stored first.
   So when running on a little-endian host, you need to swap the individual
   sub-u32's, but on a big-endian host you do.  (You still need to do
   byteswapping of the individual u32's in any case if your host and target
   endiannesses don't match.)

3. Emulation of the mnfd and absd opcodes directly poke into the second
   sub-half of their u64 argument, which is only correct on little-endian
   host platforms, since on big-endian hosts, the sign bit will be located
   in the first sub-u32.


(1) can easily be solved by adding the needed tswap32's, and this indeed
solves most of my floating point woes.  It does get somewhat ugly as those
are defined in cpu-all.h and for including that you need to pull in a lot
of other unneeded crap.

(2) and (3) are only problematic on big-endian hosts, and I'm 1600km away
from home and don't have a big-endian host nearby for testing.  I did
include suggested fixes -- in case of (2) we can just avoid swapping the
halves, and we can solve (3) by directly using a u64 constant.

(2) and (3) are obviously also problematic on 'real' big-endian hardware
when using the in-kernel version of nwfpe, and indeed, they were recently
fixed in 2.6 mainline by the following patches:
        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=2046/1
        http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=2178/1

It would be nice if someone with a big-endian host nearby could give this
patch a try.  I do have gcc sources and all that on my laptop so I can
supply BE and LE ARM binaries for testing if that'd help.


cheers,
Lennert



diff -urN qemu-20041230/target-arm/nwfpe/fpa11.h 
qemu-20041230-fpfix/target-arm/nwfpe/fpa11.h
--- qemu-20041230/target-arm/nwfpe/fpa11.h      2004-02-16 22:43:58.000000000 
+0100
+++ qemu-20041230-fpfix/target-arm/nwfpe/fpa11.h        2004-12-31 
13:49:11.668666696 +0100
@@ -22,6 +22,10 @@
 #ifndef __FPA11_H__
 #define __FPA11_H__
 
+#include <stdio.h>
+#include <sys/types.h>
+#include "cpu.h"
+
 #define GET_FPA11() (qemufpa)
 
 /*
@@ -87,8 +91,8 @@
 extern void SetRoundingMode(const unsigned int);
 extern void SetRoundingPrecision(const unsigned int);
 
-#define get_user(x,y) ((x)=*(y))
-#define put_user(x,y) (*(y)=(x))
+#define get_user(x,y) ({if (sizeof(x) != sizeof(int)) __undef_blah(); 
(x)=tswap32(*(y));})
+#define put_user(x,y) ({if (sizeof(x) != sizeof(int)) __undef_blah(); 
*(y)=tswap32(x);})
 static inline unsigned int readRegister(unsigned int reg)
 {
     return (user_registers[(reg)]);


diff -urN qemu-20041230/target-arm/nwfpe/fpa11_cpdt.c 
qemu-20041230-fpfix/target-arm/nwfpe/fpa11_cpdt.c
--- qemu-20041230/target-arm/nwfpe/fpa11_cpdt.c 2004-02-16 22:43:58.000000000 
+0100
+++ qemu-20041230-fpfix/target-arm/nwfpe/fpa11_cpdt.c   2004-12-31 
14:09:11.985190848 +0100
@@ -43,8 +43,13 @@
    unsigned int *p;
    p = (unsigned int*)&fpa11->fpreg[Fn].fDouble;
    fpa11->fType[Fn] = typeDouble;
+#if WORDS_BIGENDIAN == 1
+   get_user(p[0], &pMem[0]); /* sign & exponent */
+   get_user(p[1], &pMem[1]);
+#else
    get_user(p[0], &pMem[1]);
    get_user(p[1], &pMem[0]); /* sign & exponent */
+#endif
 }   
 
 static inline


diff -urN qemu-20041230/target-arm/nwfpe/double_cpdo.c 
qemu-20041230-fpfix/target-arm/nwfpe/double_cpdo.c
--- qemu-20041230/target-arm/nwfpe/double_cpdo.c        2004-02-16 
22:43:58.000000000 +0100
+++ qemu-20041230-fpfix/target-arm/nwfpe/double_cpdo.c  2004-12-31 
13:49:35.397059432 +0100
@@ -148,19 +148,11 @@
       break;
 
       case MNF_CODE:
-      {
-         unsigned int *p = (unsigned int*)&rFm;
-         p[1] ^= 0x80000000;
-         fpa11->fpreg[Fd].fDouble = rFm;
-      }
+         fpa11->fpreg[Fd].fDouble = rFm ^ 0x8000000000000000ULL;
       break;
 
       case ABS_CODE:
-      {
-         unsigned int *p = (unsigned int*)&rFm;
-         p[1] &= 0x7fffffff;
-         fpa11->fpreg[Fd].fDouble = rFm;
-      }
+         fpa11->fpreg[Fd].fDouble = rFm & 0x7fffffffffffffffULL;
       break;
 
       case RND_CODE:






reply via email to

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