Commit f148af25 authored by Richard Purdie's avatar Richard Purdie Committed by Russell King

[PATCH] ARM: 2837/2: Re: ARM: Make NWFPE preempt safe

Patch from Richard Purdie

NWFPE used global variables which meant it wasn't safe for use with
preemptive kernels. This patch removes them and communicates the
information between functions in a preempt safe manner. Generation
of some exceptions was broken and this has also been corrected.
Tests with glibc's maths test suite show no change in the results
before/after this patch.
Signed-off-by: default avatarRichard Purdie <rpurdie@rpsys.net>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 1fcf8448
...@@ -40,17 +40,17 @@ float64 float64_arccos(float64 rFm); ...@@ -40,17 +40,17 @@ float64 float64_arccos(float64 rFm);
float64 float64_pow(float64 rFn, float64 rFm); float64 float64_pow(float64 rFn, float64 rFm);
float64 float64_pol(float64 rFn, float64 rFm); float64 float64_pol(float64 rFn, float64 rFm);
static float64 float64_rsf(float64 rFn, float64 rFm) static float64 float64_rsf(struct roundingData *roundData, float64 rFn, float64 rFm)
{ {
return float64_sub(rFm, rFn); return float64_sub(roundData, rFm, rFn);
} }
static float64 float64_rdv(float64 rFn, float64 rFm) static float64 float64_rdv(struct roundingData *roundData, float64 rFn, float64 rFm)
{ {
return float64_div(rFm, rFn); return float64_div(roundData, rFm, rFn);
} }
static float64 (*const dyadic_double[16])(float64 rFn, float64 rFm) = { static float64 (*const dyadic_double[16])(struct roundingData*, float64 rFn, float64 rFm) = {
[ADF_CODE >> 20] = float64_add, [ADF_CODE >> 20] = float64_add,
[MUF_CODE >> 20] = float64_mul, [MUF_CODE >> 20] = float64_mul,
[SUF_CODE >> 20] = float64_sub, [SUF_CODE >> 20] = float64_sub,
...@@ -65,12 +65,12 @@ static float64 (*const dyadic_double[16])(float64 rFn, float64 rFm) = { ...@@ -65,12 +65,12 @@ static float64 (*const dyadic_double[16])(float64 rFn, float64 rFm) = {
[FRD_CODE >> 20] = float64_rdv, [FRD_CODE >> 20] = float64_rdv,
}; };
static float64 float64_mvf(float64 rFm) static float64 float64_mvf(struct roundingData *roundData,float64 rFm)
{ {
return rFm; return rFm;
} }
static float64 float64_mnf(float64 rFm) static float64 float64_mnf(struct roundingData *roundData,float64 rFm)
{ {
union float64_components u; union float64_components u;
...@@ -84,7 +84,7 @@ static float64 float64_mnf(float64 rFm) ...@@ -84,7 +84,7 @@ static float64 float64_mnf(float64 rFm)
return u.f64; return u.f64;
} }
static float64 float64_abs(float64 rFm) static float64 float64_abs(struct roundingData *roundData,float64 rFm)
{ {
union float64_components u; union float64_components u;
...@@ -98,7 +98,7 @@ static float64 float64_abs(float64 rFm) ...@@ -98,7 +98,7 @@ static float64 float64_abs(float64 rFm)
return u.f64; return u.f64;
} }
static float64 (*const monadic_double[16])(float64 rFm) = { static float64 (*const monadic_double[16])(struct roundingData *, float64 rFm) = {
[MVF_CODE >> 20] = float64_mvf, [MVF_CODE >> 20] = float64_mvf,
[MNF_CODE >> 20] = float64_mnf, [MNF_CODE >> 20] = float64_mnf,
[ABS_CODE >> 20] = float64_abs, [ABS_CODE >> 20] = float64_abs,
...@@ -108,7 +108,7 @@ static float64 (*const monadic_double[16])(float64 rFm) = { ...@@ -108,7 +108,7 @@ static float64 (*const monadic_double[16])(float64 rFm) = {
[NRM_CODE >> 20] = float64_mvf, [NRM_CODE >> 20] = float64_mvf,
}; };
unsigned int DoubleCPDO(const unsigned int opcode, FPREG * rFd) unsigned int DoubleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
float64 rFm; float64 rFm;
...@@ -151,13 +151,13 @@ unsigned int DoubleCPDO(const unsigned int opcode, FPREG * rFd) ...@@ -151,13 +151,13 @@ unsigned int DoubleCPDO(const unsigned int opcode, FPREG * rFd)
} }
if (dyadic_double[opc_mask_shift]) { if (dyadic_double[opc_mask_shift]) {
rFd->fDouble = dyadic_double[opc_mask_shift](rFn, rFm); rFd->fDouble = dyadic_double[opc_mask_shift](roundData, rFn, rFm);
} else { } else {
return 0; return 0;
} }
} else { } else {
if (monadic_double[opc_mask_shift]) { if (monadic_double[opc_mask_shift]) {
rFd->fDouble = monadic_double[opc_mask_shift](rFm); rFd->fDouble = monadic_double[opc_mask_shift](roundData, rFm);
} else { } else {
return 0; return 0;
} }
......
...@@ -35,17 +35,17 @@ floatx80 floatx80_arccos(floatx80 rFm); ...@@ -35,17 +35,17 @@ floatx80 floatx80_arccos(floatx80 rFm);
floatx80 floatx80_pow(floatx80 rFn, floatx80 rFm); floatx80 floatx80_pow(floatx80 rFn, floatx80 rFm);
floatx80 floatx80_pol(floatx80 rFn, floatx80 rFm); floatx80 floatx80_pol(floatx80 rFn, floatx80 rFm);
static floatx80 floatx80_rsf(floatx80 rFn, floatx80 rFm) static floatx80 floatx80_rsf(struct roundingData *roundData, floatx80 rFn, floatx80 rFm)
{ {
return floatx80_sub(rFm, rFn); return floatx80_sub(roundData, rFm, rFn);
} }
static floatx80 floatx80_rdv(floatx80 rFn, floatx80 rFm) static floatx80 floatx80_rdv(struct roundingData *roundData, floatx80 rFn, floatx80 rFm)
{ {
return floatx80_div(rFm, rFn); return floatx80_div(roundData, rFm, rFn);
} }
static floatx80 (*const dyadic_extended[16])(floatx80 rFn, floatx80 rFm) = { static floatx80 (*const dyadic_extended[16])(struct roundingData*, floatx80 rFn, floatx80 rFm) = {
[ADF_CODE >> 20] = floatx80_add, [ADF_CODE >> 20] = floatx80_add,
[MUF_CODE >> 20] = floatx80_mul, [MUF_CODE >> 20] = floatx80_mul,
[SUF_CODE >> 20] = floatx80_sub, [SUF_CODE >> 20] = floatx80_sub,
...@@ -60,24 +60,24 @@ static floatx80 (*const dyadic_extended[16])(floatx80 rFn, floatx80 rFm) = { ...@@ -60,24 +60,24 @@ static floatx80 (*const dyadic_extended[16])(floatx80 rFn, floatx80 rFm) = {
[FRD_CODE >> 20] = floatx80_rdv, [FRD_CODE >> 20] = floatx80_rdv,
}; };
static floatx80 floatx80_mvf(floatx80 rFm) static floatx80 floatx80_mvf(struct roundingData *roundData, floatx80 rFm)
{ {
return rFm; return rFm;
} }
static floatx80 floatx80_mnf(floatx80 rFm) static floatx80 floatx80_mnf(struct roundingData *roundData, floatx80 rFm)
{ {
rFm.high ^= 0x8000; rFm.high ^= 0x8000;
return rFm; return rFm;
} }
static floatx80 floatx80_abs(floatx80 rFm) static floatx80 floatx80_abs(struct roundingData *roundData, floatx80 rFm)
{ {
rFm.high &= 0x7fff; rFm.high &= 0x7fff;
return rFm; return rFm;
} }
static floatx80 (*const monadic_extended[16])(floatx80 rFm) = { static floatx80 (*const monadic_extended[16])(struct roundingData*, floatx80 rFm) = {
[MVF_CODE >> 20] = floatx80_mvf, [MVF_CODE >> 20] = floatx80_mvf,
[MNF_CODE >> 20] = floatx80_mnf, [MNF_CODE >> 20] = floatx80_mnf,
[ABS_CODE >> 20] = floatx80_abs, [ABS_CODE >> 20] = floatx80_abs,
...@@ -87,7 +87,7 @@ static floatx80 (*const monadic_extended[16])(floatx80 rFm) = { ...@@ -87,7 +87,7 @@ static floatx80 (*const monadic_extended[16])(floatx80 rFm) = {
[NRM_CODE >> 20] = floatx80_mvf, [NRM_CODE >> 20] = floatx80_mvf,
}; };
unsigned int ExtendedCPDO(const unsigned int opcode, FPREG * rFd) unsigned int ExtendedCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
floatx80 rFm; floatx80 rFm;
...@@ -138,13 +138,13 @@ unsigned int ExtendedCPDO(const unsigned int opcode, FPREG * rFd) ...@@ -138,13 +138,13 @@ unsigned int ExtendedCPDO(const unsigned int opcode, FPREG * rFd)
} }
if (dyadic_extended[opc_mask_shift]) { if (dyadic_extended[opc_mask_shift]) {
rFd->fExtended = dyadic_extended[opc_mask_shift](rFn, rFm); rFd->fExtended = dyadic_extended[opc_mask_shift](roundData, rFn, rFm);
} else { } else {
return 0; return 0;
} }
} else { } else {
if (monadic_extended[opc_mask_shift]) { if (monadic_extended[opc_mask_shift]) {
rFd->fExtended = monadic_extended[opc_mask_shift](rFm); rFd->fExtended = monadic_extended[opc_mask_shift](roundData, rFm);
} else { } else {
return 0; return 0;
} }
......
...@@ -51,48 +51,42 @@ static void resetFPA11(void) ...@@ -51,48 +51,42 @@ static void resetFPA11(void)
fpa11->fpsr = FP_EMULATOR | BIT_AC; fpa11->fpsr = FP_EMULATOR | BIT_AC;
} }
void SetRoundingMode(const unsigned int opcode) int8 SetRoundingMode(const unsigned int opcode)
{ {
switch (opcode & MASK_ROUNDING_MODE) { switch (opcode & MASK_ROUNDING_MODE) {
default: default:
case ROUND_TO_NEAREST: case ROUND_TO_NEAREST:
float_rounding_mode = float_round_nearest_even; return float_round_nearest_even;
break;
case ROUND_TO_PLUS_INFINITY: case ROUND_TO_PLUS_INFINITY:
float_rounding_mode = float_round_up; return float_round_up;
break;
case ROUND_TO_MINUS_INFINITY: case ROUND_TO_MINUS_INFINITY:
float_rounding_mode = float_round_down; return float_round_down;
break;
case ROUND_TO_ZERO: case ROUND_TO_ZERO:
float_rounding_mode = float_round_to_zero; return float_round_to_zero;
break;
} }
} }
void SetRoundingPrecision(const unsigned int opcode) int8 SetRoundingPrecision(const unsigned int opcode)
{ {
#ifdef CONFIG_FPE_NWFPE_XP #ifdef CONFIG_FPE_NWFPE_XP
switch (opcode & MASK_ROUNDING_PRECISION) { switch (opcode & MASK_ROUNDING_PRECISION) {
case ROUND_SINGLE: case ROUND_SINGLE:
floatx80_rounding_precision = 32; return 32;
break;
case ROUND_DOUBLE: case ROUND_DOUBLE:
floatx80_rounding_precision = 64; return 64;
break;
case ROUND_EXTENDED: case ROUND_EXTENDED:
floatx80_rounding_precision = 80; return 80;
break;
default: default:
floatx80_rounding_precision = 80; return 80;
} }
#endif #endif
return 80;
} }
void nwfpe_init_fpa(union fp_state *fp) void nwfpe_init_fpa(union fp_state *fp)
...@@ -103,8 +97,6 @@ void nwfpe_init_fpa(union fp_state *fp) ...@@ -103,8 +97,6 @@ void nwfpe_init_fpa(union fp_state *fp)
#endif #endif
memset(fpa11, 0, sizeof(FPA11)); memset(fpa11, 0, sizeof(FPA11));
resetFPA11(); resetFPA11();
SetRoundingMode(ROUND_TO_NEAREST);
SetRoundingPrecision(ROUND_EXTENDED);
fpa11->initflag = 1; fpa11->initflag = 1;
} }
......
...@@ -37,6 +37,13 @@ ...@@ -37,6 +37,13 @@
/* includes */ /* includes */
#include "fpsr.h" /* FP control and status register definitions */ #include "fpsr.h" /* FP control and status register definitions */
#include "milieu.h" #include "milieu.h"
struct roundingData {
int8 mode;
int8 precision;
signed char exception;
};
#include "softfloat.h" #include "softfloat.h"
#define typeNone 0x00 #define typeNone 0x00
...@@ -84,8 +91,8 @@ typedef struct tagFPA11 { ...@@ -84,8 +91,8 @@ typedef struct tagFPA11 {
initialised. */ initialised. */
} FPA11; } FPA11;
extern void SetRoundingMode(const unsigned int); extern int8 SetRoundingMode(const unsigned int);
extern void SetRoundingPrecision(const unsigned int); extern int8 SetRoundingPrecision(const unsigned int);
extern void nwfpe_init_fpa(union fp_state *fp); extern void nwfpe_init_fpa(union fp_state *fp);
#endif #endif
...@@ -24,15 +24,16 @@ ...@@ -24,15 +24,16 @@
#include "fpa11.h" #include "fpa11.h"
#include "fpopcode.h" #include "fpopcode.h"
unsigned int SingleCPDO(const unsigned int opcode, FPREG * rFd); unsigned int SingleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
unsigned int DoubleCPDO(const unsigned int opcode, FPREG * rFd); unsigned int DoubleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
unsigned int ExtendedCPDO(const unsigned int opcode, FPREG * rFd); unsigned int ExtendedCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
unsigned int EmulateCPDO(const unsigned int opcode) unsigned int EmulateCPDO(const unsigned int opcode)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
FPREG *rFd; FPREG *rFd;
unsigned int nType, nDest, nRc; unsigned int nType, nDest, nRc;
struct roundingData roundData;
/* Get the destination size. If not valid let Linux perform /* Get the destination size. If not valid let Linux perform
an invalid instruction trap. */ an invalid instruction trap. */
...@@ -40,7 +41,9 @@ unsigned int EmulateCPDO(const unsigned int opcode) ...@@ -40,7 +41,9 @@ unsigned int EmulateCPDO(const unsigned int opcode)
if (typeNone == nDest) if (typeNone == nDest)
return 0; return 0;
SetRoundingMode(opcode); roundData.mode = SetRoundingMode(opcode);
roundData.precision = SetRoundingPrecision(opcode);
roundData.exception = 0;
/* Compare the size of the operands in Fn and Fm. /* Compare the size of the operands in Fn and Fm.
Choose the largest size and perform operations in that size, Choose the largest size and perform operations in that size,
...@@ -63,14 +66,14 @@ unsigned int EmulateCPDO(const unsigned int opcode) ...@@ -63,14 +66,14 @@ unsigned int EmulateCPDO(const unsigned int opcode)
switch (nType) { switch (nType) {
case typeSingle: case typeSingle:
nRc = SingleCPDO(opcode, rFd); nRc = SingleCPDO(&roundData, opcode, rFd);
break; break;
case typeDouble: case typeDouble:
nRc = DoubleCPDO(opcode, rFd); nRc = DoubleCPDO(&roundData, opcode, rFd);
break; break;
#ifdef CONFIG_FPE_NWFPE_XP #ifdef CONFIG_FPE_NWFPE_XP
case typeExtended: case typeExtended:
nRc = ExtendedCPDO(opcode, rFd); nRc = ExtendedCPDO(&roundData, opcode, rFd);
break; break;
#endif #endif
default: default:
...@@ -93,9 +96,9 @@ unsigned int EmulateCPDO(const unsigned int opcode) ...@@ -93,9 +96,9 @@ unsigned int EmulateCPDO(const unsigned int opcode)
case typeSingle: case typeSingle:
{ {
if (typeDouble == nType) if (typeDouble == nType)
rFd->fSingle = float64_to_float32(rFd->fDouble); rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble);
else else
rFd->fSingle = floatx80_to_float32(rFd->fExtended); rFd->fSingle = floatx80_to_float32(&roundData, rFd->fExtended);
} }
break; break;
...@@ -104,7 +107,7 @@ unsigned int EmulateCPDO(const unsigned int opcode) ...@@ -104,7 +107,7 @@ unsigned int EmulateCPDO(const unsigned int opcode)
if (typeSingle == nType) if (typeSingle == nType)
rFd->fDouble = float32_to_float64(rFd->fSingle); rFd->fDouble = float32_to_float64(rFd->fSingle);
else else
rFd->fDouble = floatx80_to_float64(rFd->fExtended); rFd->fDouble = floatx80_to_float64(&roundData, rFd->fExtended);
} }
break; break;
...@@ -121,12 +124,15 @@ unsigned int EmulateCPDO(const unsigned int opcode) ...@@ -121,12 +124,15 @@ unsigned int EmulateCPDO(const unsigned int opcode)
#else #else
if (nDest != nType) { if (nDest != nType) {
if (nDest == typeSingle) if (nDest == typeSingle)
rFd->fSingle = float64_to_float32(rFd->fDouble); rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble);
else else
rFd->fDouble = float32_to_float64(rFd->fSingle); rFd->fDouble = float32_to_float64(rFd->fSingle);
} }
#endif #endif
} }
if (roundData.exception)
float_raise(roundData.exception);
return nRc; return nRc;
} }
...@@ -96,7 +96,7 @@ static inline void loadMultiple(const unsigned int Fn, const unsigned int __user ...@@ -96,7 +96,7 @@ static inline void loadMultiple(const unsigned int Fn, const unsigned int __user
} }
} }
static inline void storeSingle(const unsigned int Fn, unsigned int __user *pMem) static inline void storeSingle(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
union { union {
...@@ -106,12 +106,12 @@ static inline void storeSingle(const unsigned int Fn, unsigned int __user *pMem) ...@@ -106,12 +106,12 @@ static inline void storeSingle(const unsigned int Fn, unsigned int __user *pMem)
switch (fpa11->fType[Fn]) { switch (fpa11->fType[Fn]) {
case typeDouble: case typeDouble:
val.f = float64_to_float32(fpa11->fpreg[Fn].fDouble); val.f = float64_to_float32(roundData, fpa11->fpreg[Fn].fDouble);
break; break;
#ifdef CONFIG_FPE_NWFPE_XP #ifdef CONFIG_FPE_NWFPE_XP
case typeExtended: case typeExtended:
val.f = floatx80_to_float32(fpa11->fpreg[Fn].fExtended); val.f = floatx80_to_float32(roundData, fpa11->fpreg[Fn].fExtended);
break; break;
#endif #endif
...@@ -122,7 +122,7 @@ static inline void storeSingle(const unsigned int Fn, unsigned int __user *pMem) ...@@ -122,7 +122,7 @@ static inline void storeSingle(const unsigned int Fn, unsigned int __user *pMem)
put_user(val.i[0], pMem); put_user(val.i[0], pMem);
} }
static inline void storeDouble(const unsigned int Fn, unsigned int __user *pMem) static inline void storeDouble(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
union { union {
...@@ -137,7 +137,7 @@ static inline void storeDouble(const unsigned int Fn, unsigned int __user *pMem) ...@@ -137,7 +137,7 @@ static inline void storeDouble(const unsigned int Fn, unsigned int __user *pMem)
#ifdef CONFIG_FPE_NWFPE_XP #ifdef CONFIG_FPE_NWFPE_XP
case typeExtended: case typeExtended:
val.f = floatx80_to_float64(fpa11->fpreg[Fn].fExtended); val.f = floatx80_to_float64(roundData, fpa11->fpreg[Fn].fExtended);
break; break;
#endif #endif
...@@ -259,8 +259,11 @@ unsigned int PerformSTF(const unsigned int opcode) ...@@ -259,8 +259,11 @@ unsigned int PerformSTF(const unsigned int opcode)
{ {
unsigned int __user *pBase, *pAddress, *pFinal; unsigned int __user *pBase, *pAddress, *pFinal;
unsigned int nRc = 1, write_back = WRITE_BACK(opcode); unsigned int nRc = 1, write_back = WRITE_BACK(opcode);
struct roundingData roundData;
SetRoundingMode(ROUND_TO_NEAREST); roundData.mode = SetRoundingMode(opcode);
roundData.precision = SetRoundingPrecision(opcode);
roundData.exception = 0;
pBase = (unsigned int __user *) readRegister(getRn(opcode)); pBase = (unsigned int __user *) readRegister(getRn(opcode));
if (REG_PC == getRn(opcode)) { if (REG_PC == getRn(opcode)) {
...@@ -281,10 +284,10 @@ unsigned int PerformSTF(const unsigned int opcode) ...@@ -281,10 +284,10 @@ unsigned int PerformSTF(const unsigned int opcode)
switch (opcode & MASK_TRANSFER_LENGTH) { switch (opcode & MASK_TRANSFER_LENGTH) {
case TRANSFER_SINGLE: case TRANSFER_SINGLE:
storeSingle(getFd(opcode), pAddress); storeSingle(&roundData, getFd(opcode), pAddress);
break; break;
case TRANSFER_DOUBLE: case TRANSFER_DOUBLE:
storeDouble(getFd(opcode), pAddress); storeDouble(&roundData, getFd(opcode), pAddress);
break; break;
#ifdef CONFIG_FPE_NWFPE_XP #ifdef CONFIG_FPE_NWFPE_XP
case TRANSFER_EXTENDED: case TRANSFER_EXTENDED:
...@@ -295,6 +298,9 @@ unsigned int PerformSTF(const unsigned int opcode) ...@@ -295,6 +298,9 @@ unsigned int PerformSTF(const unsigned int opcode)
nRc = 0; nRc = 0;
} }
if (roundData.exception)
float_raise(roundData.exception);
if (write_back) if (write_back)
writeRegister(getRn(opcode), (unsigned long) pFinal); writeRegister(getRn(opcode), (unsigned long) pFinal);
return nRc; return nRc;
......
...@@ -33,8 +33,6 @@ extern flag floatx80_is_nan(floatx80); ...@@ -33,8 +33,6 @@ extern flag floatx80_is_nan(floatx80);
extern flag float64_is_nan(float64); extern flag float64_is_nan(float64);
extern flag float32_is_nan(float32); extern flag float32_is_nan(float32);
void SetRoundingMode(const unsigned int opcode);
unsigned int PerformFLT(const unsigned int opcode); unsigned int PerformFLT(const unsigned int opcode);
unsigned int PerformFIX(const unsigned int opcode); unsigned int PerformFIX(const unsigned int opcode);
...@@ -77,14 +75,17 @@ unsigned int EmulateCPRT(const unsigned int opcode) ...@@ -77,14 +75,17 @@ unsigned int EmulateCPRT(const unsigned int opcode)
unsigned int PerformFLT(const unsigned int opcode) unsigned int PerformFLT(const unsigned int opcode)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
SetRoundingMode(opcode); struct roundingData roundData;
SetRoundingPrecision(opcode);
roundData.mode = SetRoundingMode(opcode);
roundData.precision = SetRoundingPrecision(opcode);
roundData.exception = 0;
switch (opcode & MASK_ROUNDING_PRECISION) { switch (opcode & MASK_ROUNDING_PRECISION) {
case ROUND_SINGLE: case ROUND_SINGLE:
{ {
fpa11->fType[getFn(opcode)] = typeSingle; fpa11->fType[getFn(opcode)] = typeSingle;
fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(readRegister(getRd(opcode))); fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
} }
break; break;
...@@ -108,6 +109,9 @@ unsigned int PerformFLT(const unsigned int opcode) ...@@ -108,6 +109,9 @@ unsigned int PerformFLT(const unsigned int opcode)
return 0; return 0;
} }
if (roundData.exception)
float_raise(roundData.exception);
return 1; return 1;
} }
...@@ -115,26 +119,29 @@ unsigned int PerformFIX(const unsigned int opcode) ...@@ -115,26 +119,29 @@ unsigned int PerformFIX(const unsigned int opcode)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
unsigned int Fn = getFm(opcode); unsigned int Fn = getFm(opcode);
struct roundingData roundData;
SetRoundingMode(opcode); roundData.mode = SetRoundingMode(opcode);
roundData.precision = SetRoundingPrecision(opcode);
roundData.exception = 0;
switch (fpa11->fType[Fn]) { switch (fpa11->fType[Fn]) {
case typeSingle: case typeSingle:
{ {
writeRegister(getRd(opcode), float32_to_int32(fpa11->fpreg[Fn].fSingle)); writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
} }
break; break;
case typeDouble: case typeDouble:
{ {
writeRegister(getRd(opcode), float64_to_int32(fpa11->fpreg[Fn].fDouble)); writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
} }
break; break;
#ifdef CONFIG_FPE_NWFPE_XP #ifdef CONFIG_FPE_NWFPE_XP
case typeExtended: case typeExtended:
{ {
writeRegister(getRd(opcode), floatx80_to_int32(fpa11->fpreg[Fn].fExtended)); writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
} }
break; break;
#endif #endif
...@@ -143,6 +150,9 @@ unsigned int PerformFIX(const unsigned int opcode) ...@@ -143,6 +150,9 @@ unsigned int PerformFIX(const unsigned int opcode)
return 0; return 0;
} }
if (roundData.exception)
float_raise(roundData.exception);
return 1; return 1;
} }
......
...@@ -116,8 +116,6 @@ fpmodule.c to integrate with the NetBSD kernel (I hope!). ...@@ -116,8 +116,6 @@ fpmodule.c to integrate with the NetBSD kernel (I hope!).
code to access data in user space in some other source files at the code to access data in user space in some other source files at the
moment (grep for get_user / put_user calls). --philb] moment (grep for get_user / put_user calls). --philb]
float_exception_flags is a global variable in SoftFloat.
This function is called by the SoftFloat routines to raise a floating This function is called by the SoftFloat routines to raise a floating
point exception. We check the trap enable byte in the FPSR, and raise point exception. We check the trap enable byte in the FPSR, and raise
a SIGFPE exception if necessary. If not the relevant bits in the a SIGFPE exception if necessary. If not the relevant bits in the
...@@ -129,15 +127,14 @@ void float_raise(signed char flags) ...@@ -129,15 +127,14 @@ void float_raise(signed char flags)
register unsigned int fpsr, cumulativeTraps; register unsigned int fpsr, cumulativeTraps;
#ifdef CONFIG_DEBUG_USER #ifdef CONFIG_DEBUG_USER
/* Ignore inexact errors as there are far too many of them to log */
if (flags & ~BIT_IXC)
printk(KERN_DEBUG printk(KERN_DEBUG
"NWFPE: %s[%d] takes exception %08x at %p from %08lx\n", "NWFPE: %s[%d] takes exception %08x at %p from %08lx\n",
current->comm, current->pid, flags, current->comm, current->pid, flags,
__builtin_return_address(0), GET_USERREG()->ARM_pc); __builtin_return_address(0), GET_USERREG()->ARM_pc);
#endif #endif
/* Keep SoftFloat exception flags up to date. */
float_exception_flags |= flags;
/* Read fpsr and initialize the cumulativeTraps. */ /* Read fpsr and initialize the cumulativeTraps. */
fpsr = readFPSR(); fpsr = readFPSR();
cumulativeTraps = 0; cumulativeTraps = 0;
......
...@@ -36,17 +36,17 @@ float32 float32_arccos(float32 rFm); ...@@ -36,17 +36,17 @@ float32 float32_arccos(float32 rFm);
float32 float32_pow(float32 rFn, float32 rFm); float32 float32_pow(float32 rFn, float32 rFm);
float32 float32_pol(float32 rFn, float32 rFm); float32 float32_pol(float32 rFn, float32 rFm);
static float32 float32_rsf(float32 rFn, float32 rFm) static float32 float32_rsf(struct roundingData *roundData, float32 rFn, float32 rFm)
{ {
return float32_sub(rFm, rFn); return float32_sub(roundData, rFm, rFn);
} }
static float32 float32_rdv(float32 rFn, float32 rFm) static float32 float32_rdv(struct roundingData *roundData, float32 rFn, float32 rFm)
{ {
return float32_div(rFm, rFn); return float32_div(roundData, rFm, rFn);
} }
static float32 (*const dyadic_single[16])(float32 rFn, float32 rFm) = { static float32 (*const dyadic_single[16])(struct roundingData *, float32 rFn, float32 rFm) = {
[ADF_CODE >> 20] = float32_add, [ADF_CODE >> 20] = float32_add,
[MUF_CODE >> 20] = float32_mul, [MUF_CODE >> 20] = float32_mul,
[SUF_CODE >> 20] = float32_sub, [SUF_CODE >> 20] = float32_sub,
...@@ -60,22 +60,22 @@ static float32 (*const dyadic_single[16])(float32 rFn, float32 rFm) = { ...@@ -60,22 +60,22 @@ static float32 (*const dyadic_single[16])(float32 rFn, float32 rFm) = {
[FRD_CODE >> 20] = float32_rdv, [FRD_CODE >> 20] = float32_rdv,
}; };
static float32 float32_mvf(float32 rFm) static float32 float32_mvf(struct roundingData *roundData, float32 rFm)
{ {
return rFm; return rFm;
} }
static float32 float32_mnf(float32 rFm) static float32 float32_mnf(struct roundingData *roundData, float32 rFm)
{ {
return rFm ^ 0x80000000; return rFm ^ 0x80000000;
} }
static float32 float32_abs(float32 rFm) static float32 float32_abs(struct roundingData *roundData, float32 rFm)
{ {
return rFm & 0x7fffffff; return rFm & 0x7fffffff;
} }
static float32 (*const monadic_single[16])(float32 rFm) = { static float32 (*const monadic_single[16])(struct roundingData*, float32 rFm) = {
[MVF_CODE >> 20] = float32_mvf, [MVF_CODE >> 20] = float32_mvf,
[MNF_CODE >> 20] = float32_mnf, [MNF_CODE >> 20] = float32_mnf,
[ABS_CODE >> 20] = float32_abs, [ABS_CODE >> 20] = float32_abs,
...@@ -85,7 +85,7 @@ static float32 (*const monadic_single[16])(float32 rFm) = { ...@@ -85,7 +85,7 @@ static float32 (*const monadic_single[16])(float32 rFm) = {
[NRM_CODE >> 20] = float32_mvf, [NRM_CODE >> 20] = float32_mvf,
}; };
unsigned int SingleCPDO(const unsigned int opcode, FPREG * rFd) unsigned int SingleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd)
{ {
FPA11 *fpa11 = GET_FPA11(); FPA11 *fpa11 = GET_FPA11();
float32 rFm; float32 rFm;
...@@ -108,13 +108,13 @@ unsigned int SingleCPDO(const unsigned int opcode, FPREG * rFd) ...@@ -108,13 +108,13 @@ unsigned int SingleCPDO(const unsigned int opcode, FPREG * rFd)
if (fpa11->fType[Fn] == typeSingle && if (fpa11->fType[Fn] == typeSingle &&
dyadic_single[opc_mask_shift]) { dyadic_single[opc_mask_shift]) {
rFn = fpa11->fpreg[Fn].fSingle; rFn = fpa11->fpreg[Fn].fSingle;
rFd->fSingle = dyadic_single[opc_mask_shift](rFn, rFm); rFd->fSingle = dyadic_single[opc_mask_shift](roundData, rFn, rFm);
} else { } else {
return 0; return 0;
} }
} else { } else {
if (monadic_single[opc_mask_shift]) { if (monadic_single[opc_mask_shift]) {
rFd->fSingle = monadic_single[opc_mask_shift](rFm); rFd->fSingle = monadic_single[opc_mask_shift](roundData, rFm);
} else { } else {
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -74,7 +74,7 @@ enum { ...@@ -74,7 +74,7 @@ enum {
Software IEC/IEEE floating-point rounding mode. Software IEC/IEEE floating-point rounding mode.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
extern signed char float_rounding_mode; //extern int8 float_rounding_mode;
enum { enum {
float_round_nearest_even = 0, float_round_nearest_even = 0,
float_round_to_zero = 1, float_round_to_zero = 1,
...@@ -86,7 +86,6 @@ enum { ...@@ -86,7 +86,6 @@ enum {
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Software IEC/IEEE floating-point exception flags. Software IEC/IEEE floating-point exception flags.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
extern signed char float_exception_flags;
enum { enum {
float_flag_inexact = 1, float_flag_inexact = 1,
float_flag_underflow = 2, float_flag_underflow = 2,
...@@ -99,7 +98,6 @@ ScottB: November 4, 1998 ...@@ -99,7 +98,6 @@ ScottB: November 4, 1998
Changed the enumeration to match the bit order in the FPA11. Changed the enumeration to match the bit order in the FPA11.
*/ */
extern signed char float_exception_flags;
enum { enum {
float_flag_invalid = 1, float_flag_invalid = 1,
float_flag_divbyzero = 2, float_flag_divbyzero = 2,
...@@ -121,7 +119,7 @@ void float_raise( signed char ); ...@@ -121,7 +119,7 @@ void float_raise( signed char );
Software IEC/IEEE integer-to-floating-point conversion routines. Software IEC/IEEE integer-to-floating-point conversion routines.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
float32 int32_to_float32( signed int ); float32 int32_to_float32( struct roundingData *, signed int );
float64 int32_to_float64( signed int ); float64 int32_to_float64( signed int );
#ifdef FLOATX80 #ifdef FLOATX80
floatx80 int32_to_floatx80( signed int ); floatx80 int32_to_floatx80( signed int );
...@@ -132,7 +130,7 @@ floatx80 int32_to_floatx80( signed int ); ...@@ -132,7 +130,7 @@ floatx80 int32_to_floatx80( signed int );
Software IEC/IEEE single-precision conversion routines. Software IEC/IEEE single-precision conversion routines.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
signed int float32_to_int32( float32 ); signed int float32_to_int32( struct roundingData *, float32 );
signed int float32_to_int32_round_to_zero( float32 ); signed int float32_to_int32_round_to_zero( float32 );
float64 float32_to_float64( float32 ); float64 float32_to_float64( float32 );
#ifdef FLOATX80 #ifdef FLOATX80
...@@ -144,13 +142,13 @@ floatx80 float32_to_floatx80( float32 ); ...@@ -144,13 +142,13 @@ floatx80 float32_to_floatx80( float32 );
Software IEC/IEEE single-precision operations. Software IEC/IEEE single-precision operations.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
float32 float32_round_to_int( float32 ); float32 float32_round_to_int( struct roundingData*, float32 );
float32 float32_add( float32, float32 ); float32 float32_add( struct roundingData *, float32, float32 );
float32 float32_sub( float32, float32 ); float32 float32_sub( struct roundingData *, float32, float32 );
float32 float32_mul( float32, float32 ); float32 float32_mul( struct roundingData *, float32, float32 );
float32 float32_div( float32, float32 ); float32 float32_div( struct roundingData *, float32, float32 );
float32 float32_rem( float32, float32 ); float32 float32_rem( struct roundingData *, float32, float32 );
float32 float32_sqrt( float32 ); float32 float32_sqrt( struct roundingData*, float32 );
char float32_eq( float32, float32 ); char float32_eq( float32, float32 );
char float32_le( float32, float32 ); char float32_le( float32, float32 );
char float32_lt( float32, float32 ); char float32_lt( float32, float32 );
...@@ -164,9 +162,9 @@ char float32_is_signaling_nan( float32 ); ...@@ -164,9 +162,9 @@ char float32_is_signaling_nan( float32 );
Software IEC/IEEE double-precision conversion routines. Software IEC/IEEE double-precision conversion routines.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
signed int float64_to_int32( float64 ); signed int float64_to_int32( struct roundingData *, float64 );
signed int float64_to_int32_round_to_zero( float64 ); signed int float64_to_int32_round_to_zero( float64 );
float32 float64_to_float32( float64 ); float32 float64_to_float32( struct roundingData *, float64 );
#ifdef FLOATX80 #ifdef FLOATX80
floatx80 float64_to_floatx80( float64 ); floatx80 float64_to_floatx80( float64 );
#endif #endif
...@@ -176,13 +174,13 @@ floatx80 float64_to_floatx80( float64 ); ...@@ -176,13 +174,13 @@ floatx80 float64_to_floatx80( float64 );
Software IEC/IEEE double-precision operations. Software IEC/IEEE double-precision operations.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
float64 float64_round_to_int( float64 ); float64 float64_round_to_int( struct roundingData *, float64 );
float64 float64_add( float64, float64 ); float64 float64_add( struct roundingData *, float64, float64 );
float64 float64_sub( float64, float64 ); float64 float64_sub( struct roundingData *, float64, float64 );
float64 float64_mul( float64, float64 ); float64 float64_mul( struct roundingData *, float64, float64 );
float64 float64_div( float64, float64 ); float64 float64_div( struct roundingData *, float64, float64 );
float64 float64_rem( float64, float64 ); float64 float64_rem( struct roundingData *, float64, float64 );
float64 float64_sqrt( float64 ); float64 float64_sqrt( struct roundingData *, float64 );
char float64_eq( float64, float64 ); char float64_eq( float64, float64 );
char float64_le( float64, float64 ); char float64_le( float64, float64 );
char float64_lt( float64, float64 ); char float64_lt( float64, float64 );
...@@ -198,31 +196,23 @@ char float64_is_signaling_nan( float64 ); ...@@ -198,31 +196,23 @@ char float64_is_signaling_nan( float64 );
Software IEC/IEEE extended double-precision conversion routines. Software IEC/IEEE extended double-precision conversion routines.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
signed int floatx80_to_int32( floatx80 ); signed int floatx80_to_int32( struct roundingData *, floatx80 );
signed int floatx80_to_int32_round_to_zero( floatx80 ); signed int floatx80_to_int32_round_to_zero( floatx80 );
float32 floatx80_to_float32( floatx80 ); float32 floatx80_to_float32( struct roundingData *, floatx80 );
float64 floatx80_to_float64( floatx80 ); float64 floatx80_to_float64( struct roundingData *, floatx80 );
/*
-------------------------------------------------------------------------------
Software IEC/IEEE extended double-precision rounding precision. Valid
values are 32, 64, and 80.
-------------------------------------------------------------------------------
*/
extern signed char floatx80_rounding_precision;
/* /*
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
Software IEC/IEEE extended double-precision operations. Software IEC/IEEE extended double-precision operations.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
*/ */
floatx80 floatx80_round_to_int( floatx80 ); floatx80 floatx80_round_to_int( struct roundingData *, floatx80 );
floatx80 floatx80_add( floatx80, floatx80 ); floatx80 floatx80_add( struct roundingData *, floatx80, floatx80 );
floatx80 floatx80_sub( floatx80, floatx80 ); floatx80 floatx80_sub( struct roundingData *, floatx80, floatx80 );
floatx80 floatx80_mul( floatx80, floatx80 ); floatx80 floatx80_mul( struct roundingData *, floatx80, floatx80 );
floatx80 floatx80_div( floatx80, floatx80 ); floatx80 floatx80_div( struct roundingData *, floatx80, floatx80 );
floatx80 floatx80_rem( floatx80, floatx80 ); floatx80 floatx80_rem( struct roundingData *, floatx80, floatx80 );
floatx80 floatx80_sqrt( floatx80 ); floatx80 floatx80_sqrt( struct roundingData *, floatx80 );
char floatx80_eq( floatx80, floatx80 ); char floatx80_eq( floatx80, floatx80 );
char floatx80_le( floatx80, floatx80 ); char floatx80_le( floatx80, floatx80 );
char floatx80_lt( floatx80, floatx80 ); char floatx80_lt( floatx80, floatx80 );
......
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