Commit ad889c6b authored by Russell King's avatar Russell King

Fix nwfpe so GDB can debug user space floating point again.

Patch 960/1 (Peter Teichmann):
   NWFPE patch to be more compliant to IEEE-754

1. The RND/URD instruction was handled as int_to_float(float_to_int
   (number)) which is wrong because it only works for floating point
   numbers that fit in an integer.

2. The FLT instruction was setting the rounding precision for
   extended precision calculations, which is not necessary
   (probably a historic relict) but has undesirable side effects
   on all extended precision calculations.
parent a6560a26
2002-01-19 Russell King <rmk@arm.linux.org.uk>
* fpa11.h - Add documentation
- remove userRegisters pointer from this structure.
- add new method to obtain integer register values.
* softfloat.c - Remove float128
* softfloat.h - Remove float128
* softfloat-specialize - Remove float128
* The FPA11 structure is not a kernel-specific data structure.
It is used by users of ptrace to examine the values of the
floating point registers. Therefore, any changes to the
FPA11 structure (size or position of elements contained
within) have to be well thought out.
* Since 128-bit float requires the FPA11 structure to change
size, it has been removed. 128-bit float is currently unused,
and needs various things to be re-worked so that we won't
overflow the available space in the task structure.
* The changes are designed to break any patch that goes on top
of this code, so that the authors properly review their changes.
1999-08-19 Scott Bambrough <scottb@netwinder.org>
* fpmodule.c - Changed version number to 0.95
......
......@@ -19,9 +19,9 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpa11.h"
float64 float64_exp(float64 Fm);
float64 float64_ln(float64 Fm);
......@@ -165,8 +165,7 @@ unsigned int DoubleCPDO(const unsigned int opcode)
case RND_CODE:
case URD_CODE:
fpa11->fpreg[Fd].fDouble =
int32_to_float64(float64_to_int32(rFm));
fpa11->fpreg[Fd].fDouble = float64_round_to_int(rFm);
break;
case SQT_CODE:
......
......@@ -52,8 +52,6 @@ This routine does three things:
1) The kernel has created a struct pt_regs on the stack and saved the
user registers into it. See /usr/include/asm/proc/ptrace.h for details.
The emulator code uses userRegisters as the base of an array of words
from which the contents of the registers can be extracted.
2) It calls EmulateAll to emulate a floating point instruction.
EmulateAll returns 1 if the emulation was successful, or 0 if not.
......@@ -72,14 +70,9 @@ several floating point instructions. */
.globl nwfpe_enter
nwfpe_enter:
/* ?? Could put userRegisters and fpa11 into fixed regs during
emulation. This would reduce load/store overhead at the expense
of stealing two regs from the register allocator. Not sure if
it's worth it. */
str sp, [r10] @ Store the user registers pointer in the fpa11 structure.
mov r4, lr @ save the failure-return addresses
mov sl, sp
mov r0, r10
bl FPA11_CheckInit @ check to see if we are initialised
ldr r5, [sp, #60] @ get contents of PC;
......
......@@ -65,18 +65,10 @@ several floating point instructions. */
.globl nwfpe_enter
nwfpe_enter:
ldr r4, =userRegisters
str sp, [r4] @ save pointer to user regs
mov sl, sp
bl FPA11_CheckInit @ check to see if we are initialised
mov r10, sp, lsr #13 @ find workspace
mov r10, r10, lsl #13
add r10, r10, #TSS_FPESAVE
ldr r4, =fpa11
str r10, [r4] @ store pointer to our state
mov r4, sp @ use r4 for local pointer
ldr r5, [r4, #60] @ get contents of PC
ldr r5, [sp, #60] @ get contents of PC
bic r5, r5, #0xfc000003
ldr r0, [r5, #-4] @ get actual instruction into r0
bl EmulateAll @ emulate the instruction
......@@ -93,10 +85,10 @@ next:
teqne r2, #0x0E000000
bne ret_from_exception @ return ok if not a fp insn
ldr r9, [r4, #60] @ get new condition codes
ldr r9, [sp, #60] @ get new condition codes
and r9, r9, #0xfc000003
orr r7, r5, r9
str r7, [r4, #60] @ update PC copy in regs
str r7, [sp, #60] @ update PC copy in regs
mov r0, r6 @ save a copy
mov r1, r9 @ fetch the condition codes
......
......@@ -19,9 +19,9 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpa11.h"
floatx80 floatx80_exp(floatx80 Fm);
floatx80 floatx80_ln(floatx80 Fm);
......@@ -157,8 +157,7 @@ unsigned int ExtendedCPDO(const unsigned int opcode)
case RND_CODE:
case URD_CODE:
fpa11->fpreg[Fd].fExtended =
int32_to_floatx80(floatx80_to_int32(rFm));
fpa11->fpreg[Fd].fExtended = floatx80_round_to_int(rFm);
break;
case SQT_CODE:
......
......@@ -18,8 +18,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/compiler.h>
#include <asm/system.h>
#include "fpa11.h"
#include "fpopcode.h"
......@@ -27,6 +25,9 @@
#include "fpmodule.h"
#include "fpmodule.inl"
#include <linux/compiler.h>
#include <asm/system.h>
/* forward declarations */
unsigned int EmulateCPDO(const unsigned int);
unsigned int EmulateCPDT(const unsigned int);
......@@ -56,6 +57,7 @@ void resetFPA11(void)
void SetRoundingMode(const unsigned int opcode)
{
#if MAINTAIN_FPCR
FPA11 *fpa11 = GET_FPA11();
fpa11->fpcr &= ~MASK_ROUNDING_MODE;
#endif
switch (opcode & MASK_ROUNDING_MODE)
......@@ -94,6 +96,7 @@ void SetRoundingMode(const unsigned int opcode)
void SetRoundingPrecision(const unsigned int opcode)
{
#if MAINTAIN_FPCR
FPA11 *fpa11 = GET_FPA11();
fpa11->fpcr &= ~MASK_ROUNDING_PRECISION;
#endif
switch (opcode & MASK_ROUNDING_PRECISION)
......@@ -123,8 +126,9 @@ void SetRoundingPrecision(const unsigned int opcode)
}
}
void FPA11_CheckInit(FPA11 *fpa11)
void FPA11_CheckInit(void)
{
FPA11 *fpa11 = GET_FPA11();
if (unlikely(fpa11->initflag == 0))
{
resetFPA11();
......
......@@ -22,45 +22,66 @@
#ifndef __FPA11_H__
#define __FPA11_H__
#define GET_FPA11() ((FPA11 *)(&current_thread_info()->fpstate))
/*
* The processes registers are always at the very top of the 8K
* stack+task struct. Use the same method as 'current' uses to
* reach them.
*/
register unsigned int *user_registers asm("sl");
#define GET_USERREG() (user_registers)
#include <linux/thread_info.h>
/* includes */
#include "fpsr.h" /* FP control and status register definitions */
#include "softfloat.h"
/* Need task_struct */
#include <linux/sched.h>
#define typeNone 0x00
#define typeSingle 0x01
#define typeDouble 0x02
#define typeExtended 0x03
/*
* This must be no more and no less than 12 bytes.
*/
typedef union tagFPREG {
float32 fSingle;
float64 fDouble;
floatx80 fExtended;
float64 fDouble;
float32 fSingle;
} FPREG;
/* FPA11 device model */
/*
* FPA11 device model.
*
* This structure is exported to user space. Do not re-order.
* Only add new stuff to the end, and do not change the size of
* any element. Elements of this structure are used by user
* space, and must match struct user_fp in include/asm-arm/user.h.
* We include the byte offsets below for documentation purposes.
*
* The size of this structure and FPREG are checked by fpmodule.c
* on initialisation. If the rules have been broken, NWFPE will
* not initialise.
*/
typedef struct tagFPA11 {
unsigned int *userRegisters;
FPREG fpreg[8]; /* 8 floating point registers */
FPSR fpsr; /* floating point status register */
FPCR fpcr; /* floating point control register */
unsigned char fType[8]; /* type of floating point value held in
floating point registers. One of none
single, double or extended. */
int initflag; /* this is special. The kernel guarantees
to set it to 0 when a thread is launched,
so we can use it to detect whether this
instance of the emulator needs to be
initialised. */
/* 0 */ FPREG fpreg[8]; /* 8 floating point registers */
/* 96 */ FPSR fpsr; /* floating point status register */
/* 100 */ FPCR fpcr; /* floating point control register */
/* 104 */ unsigned char fType[8]; /* type of floating point value held in
floating point registers. One of none
single, double or extended. */
/* 112 */ int initflag; /* this is special. The kernel guarantees
to set it to 0 when a thread is launched,
so we can use it to detect whether this
instance of the emulator needs to be
initialised. */
} FPA11;
extern void resetFPA11(void);
extern void SetRoundingMode(const unsigned int);
extern void SetRoundingPrecision(const unsigned int);
#define GET_FPA11() ((FPA11 *)(&current->thread.fpstate))
#define GET_USERREG() (GET_FPA11()->userRegisters)
#endif
......@@ -20,9 +20,9 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpa11.h"
#include "fpmodule.h"
#include "fpmodule.inl"
......
......@@ -20,10 +20,10 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "milieu.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpa11.h"
#include "fpa11.inl"
#include "fpmodule.h"
#include "fpmodule.inl"
......@@ -82,8 +82,7 @@ unsigned int PerformFLT(const unsigned int opcode)
unsigned int nRc = 1;
SetRoundingMode(opcode);
SetRoundingPrecision(opcode);
switch (opcode & MASK_ROUNDING_PRECISION)
{
case ROUND_SINGLE:
......
......@@ -21,6 +21,8 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include <linux/module.h>
#include <linux/version.h>
#include <linux/config.h>
......@@ -36,7 +38,6 @@
#include "softfloat.h"
#include "fpopcode.h"
#include "fpmodule.h"
#include "fpa11.h"
#include "fpa11.inl"
/* kernel symbols required for signal handling */
......@@ -86,6 +87,11 @@ static int __init fpe_init(void)
return -EINVAL;
}
if (sizeof(FPREG) != 12) {
printk(KERN_ERR "nwfpe: bad register size\n");
return -EINVAL;
}
#ifdef MODULE
if (!mod_member_present(&__this_module, can_unload))
return -EINVAL;
......
......@@ -19,10 +19,10 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpsr.h"
#include "fpa11.h"
#include "fpmodule.h"
#include "fpmodule.inl"
......
......@@ -19,9 +19,9 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "fpa11.h"
#include "softfloat.h"
#include "fpopcode.h"
#include "fpa11.h"
float32 float32_exp(float32 Fm);
float32 float32_ln(float32 Fm);
......@@ -139,8 +139,7 @@ unsigned int SingleCPDO(const unsigned int opcode)
case RND_CODE:
case URD_CODE:
fpa11->fpreg[Fd].fSingle =
int32_to_float32(float32_to_int32(rFm));
fpa11->fpreg[Fd].fSingle = float32_round_to_int(rFm);
break;
case SQT_CODE:
......
......@@ -364,108 +364,3 @@ static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b )
}
#endif
#ifdef FLOAT128
/*
-------------------------------------------------------------------------------
The pattern for a default generated quadruple-precision NaN. The `high' and
`low' values hold the most- and least-significant bits, respectively.
-------------------------------------------------------------------------------
*/
#define float128_default_nan_high LIT64( 0xFFFFFFFFFFFFFFFF )
#define float128_default_nan_low LIT64( 0xFFFFFFFFFFFFFFFF )
/*
-------------------------------------------------------------------------------
Returns 1 if the quadruple-precision floating-point value `a' is a NaN;
otherwise returns 0.
-------------------------------------------------------------------------------
*/
flag float128_is_nan( float128 a )
{
return
( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
&& ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
}
/*
-------------------------------------------------------------------------------
Returns 1 if the quadruple-precision floating-point value `a' is a
signaling NaN; otherwise returns 0.
-------------------------------------------------------------------------------
*/
flag float128_is_signaling_nan( float128 a )
{
return
( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
&& ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
}
/*
-------------------------------------------------------------------------------
Returns the result of converting the quadruple-precision floating-point NaN
`a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
exception is raised.
-------------------------------------------------------------------------------
*/
static commonNaNT float128ToCommonNaN( float128 a )
{
commonNaNT z;
if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
z.sign = a.high>>63;
shortShift128Left( a.high, a.low, 16, &z.high, &z.low );
return z;
}
/*
-------------------------------------------------------------------------------
Returns the result of converting the canonical NaN `a' to the quadruple-
precision floating-point format.
-------------------------------------------------------------------------------
*/
static float128 commonNaNToFloat128( commonNaNT a )
{
float128 z;
shift128Right( a.high, a.low, 16, &z.high, &z.low );
z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 );
return z;
}
/*
-------------------------------------------------------------------------------
Takes two quadruple-precision floating-point values `a' and `b', one of
which is a NaN, and returns the appropriate NaN result. If either `a' or
`b' is a signaling NaN, the invalid exception is raised.
-------------------------------------------------------------------------------
*/
static float128 propagateFloat128NaN( float128 a, float128 b )
{
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
aIsNaN = float128_is_nan( a );
aIsSignalingNaN = float128_is_signaling_nan( a );
bIsNaN = float128_is_nan( b );
bIsSignalingNaN = float128_is_signaling_nan( b );
a.high |= LIT64( 0x0000800000000000 );
b.high |= LIT64( 0x0000800000000000 );
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
if ( aIsNaN ) {
return ( aIsSignalingNaN & bIsNaN ) ? b : a;
}
else {
return b;
}
}
#endif
This diff is collapsed.
......@@ -37,12 +37,10 @@ this code that are retained.
The macro `FLOATX80' must be defined to enable the extended double-precision
floating-point format `floatx80'. If this macro is not defined, the
`floatx80' type will not be defined, and none of the functions that either
input or output the `floatx80' type will be defined. The same applies to
the `FLOAT128' macro and the quadruple-precision format `float128'.
input or output the `floatx80' type will be defined.
-------------------------------------------------------------------------------
*/
#define FLOATX80
/* #define FLOAT128 */
/*
-------------------------------------------------------------------------------
......@@ -51,17 +49,10 @@ Software IEC/IEEE floating-point types.
*/
typedef unsigned long int float32;
typedef unsigned long long float64;
#ifdef FLOATX80
typedef struct {
unsigned short high;
unsigned long long low;
} floatx80;
#endif
#ifdef FLOAT128
typedef struct {
unsigned long long high, low;
} float128;
#endif
/*
-------------------------------------------------------------------------------
......@@ -131,9 +122,6 @@ float64 int32_to_float64( signed int );
#ifdef FLOATX80
floatx80 int32_to_floatx80( signed int );
#endif
#ifdef FLOAT128
float128 int32_to_float128( signed int );
#endif
/*
-------------------------------------------------------------------------------
......@@ -146,9 +134,6 @@ float64 float32_to_float64( float32 );
#ifdef FLOATX80
floatx80 float32_to_floatx80( float32 );
#endif
#ifdef FLOAT128
float128 float32_to_float128( float32 );
#endif
/*
-------------------------------------------------------------------------------
......@@ -181,9 +166,6 @@ float32 float64_to_float32( float64 );
#ifdef FLOATX80
floatx80 float64_to_floatx80( float64 );
#endif
#ifdef FLOAT128
float128 float64_to_float128( float64 );
#endif
/*
-------------------------------------------------------------------------------
......@@ -216,9 +198,6 @@ signed int floatx80_to_int32( floatx80 );
signed int floatx80_to_int32_round_to_zero( floatx80 );
float32 floatx80_to_float32( floatx80 );
float64 floatx80_to_float64( floatx80 );
#ifdef FLOAT128
float128 floatx80_to_float128( floatx80 );
#endif
/*
-------------------------------------------------------------------------------
......@@ -250,41 +229,4 @@ char floatx80_is_signaling_nan( floatx80 );
#endif
#ifdef FLOAT128
/*
-------------------------------------------------------------------------------
Software IEC/IEEE quadruple-precision conversion routines.
-------------------------------------------------------------------------------
*/
signed int float128_to_int32( float128 );
signed int float128_to_int32_round_to_zero( float128 );
float32 float128_to_float32( float128 );
float64 float128_to_float64( float128 );
#ifdef FLOATX80
floatx80 float128_to_floatx80( float128 );
#endif
/*
-------------------------------------------------------------------------------
Software IEC/IEEE quadruple-precision operations.
-------------------------------------------------------------------------------
*/
float128 float128_round_to_int( float128 );
float128 float128_add( float128, float128 );
float128 float128_sub( float128, float128 );
float128 float128_mul( float128, float128 );
float128 float128_div( float128, float128 );
float128 float128_rem( float128, float128 );
float128 float128_sqrt( float128 );
char float128_eq( float128, float128 );
char float128_le( float128, float128 );
char float128_lt( float128, float128 );
char float128_eq_signaling( float128, float128 );
char float128_le_quiet( float128, float128 );
char float128_lt_quiet( float128, float128 );
char float128_is_signaling_nan( float128 );
#endif
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment