fpu: Handle m68k extended precision denormals properly
Motorola treats denormals with explicit integer bit set as having unbiased exponent 0, unlike Intel which treats it as having unbiased exponent 1 (more like all other IEEE formats that have no explicit integer bit). Add a flag on FloatFmt to differentiate the behaviour. Reported-by: Keith Packard <keithp@keithp.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
parent
00f9ef8f3d
commit
722460652b
@ -118,7 +118,8 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status,
|
|||||||
} else {
|
} else {
|
||||||
int shift = frac_normalize(p);
|
int shift = frac_normalize(p);
|
||||||
p->cls = float_class_normal;
|
p->cls = float_class_normal;
|
||||||
p->exp = fmt->frac_shift - fmt->exp_bias - shift + 1;
|
p->exp = fmt->frac_shift - fmt->exp_bias
|
||||||
|
- shift + !fmt->m68k_denormal;
|
||||||
}
|
}
|
||||||
} else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) {
|
} else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) {
|
||||||
p->cls = float_class_normal;
|
p->cls = float_class_normal;
|
||||||
@ -256,7 +257,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
|
|||||||
is_tiny = !frac_addi(&discard, p, inc);
|
is_tiny = !frac_addi(&discard, p, inc);
|
||||||
}
|
}
|
||||||
|
|
||||||
frac_shrjam(p, 1 - exp);
|
frac_shrjam(p, !fmt->m68k_denormal - exp);
|
||||||
|
|
||||||
if (p->frac_lo & round_mask) {
|
if (p->frac_lo & round_mask) {
|
||||||
/* Need to recompute round-to-even/round-to-odd. */
|
/* Need to recompute round-to-even/round-to-odd. */
|
||||||
@ -287,7 +288,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s,
|
|||||||
p->frac_lo &= ~round_mask;
|
p->frac_lo &= ~round_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) != 0;
|
exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !fmt->m68k_denormal;
|
||||||
frac_shr(p, frac_shift);
|
frac_shr(p, frac_shift);
|
||||||
|
|
||||||
if (is_tiny && (flags & float_flag_inexact)) {
|
if (is_tiny && (flags & float_flag_inexact)) {
|
||||||
|
@ -517,6 +517,7 @@ typedef struct {
|
|||||||
* round_mask: bits below lsb which must be rounded
|
* round_mask: bits below lsb which must be rounded
|
||||||
* The following optional modifiers are available:
|
* The following optional modifiers are available:
|
||||||
* arm_althp: handle ARM Alternative Half Precision
|
* arm_althp: handle ARM Alternative Half Precision
|
||||||
|
* m68k_denormal: explicit integer bit for extended precision may be 1
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int exp_size;
|
int exp_size;
|
||||||
@ -526,6 +527,7 @@ typedef struct {
|
|||||||
int frac_size;
|
int frac_size;
|
||||||
int frac_shift;
|
int frac_shift;
|
||||||
bool arm_althp;
|
bool arm_althp;
|
||||||
|
bool m68k_denormal;
|
||||||
uint64_t round_mask;
|
uint64_t round_mask;
|
||||||
} FloatFmt;
|
} FloatFmt;
|
||||||
|
|
||||||
@ -576,7 +578,12 @@ static const FloatFmt float128_params = {
|
|||||||
static const FloatFmt floatx80_params[3] = {
|
static const FloatFmt floatx80_params[3] = {
|
||||||
[floatx80_precision_s] = { FLOATX80_PARAMS(23) },
|
[floatx80_precision_s] = { FLOATX80_PARAMS(23) },
|
||||||
[floatx80_precision_d] = { FLOATX80_PARAMS(52) },
|
[floatx80_precision_d] = { FLOATX80_PARAMS(52) },
|
||||||
[floatx80_precision_x] = { FLOATX80_PARAMS(64) },
|
[floatx80_precision_x] = {
|
||||||
|
FLOATX80_PARAMS(64),
|
||||||
|
#ifdef TARGET_M68K
|
||||||
|
.m68k_denormal = true,
|
||||||
|
#endif
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Unpack a float to parts, but do not canonicalize. */
|
/* Unpack a float to parts, but do not canonicalize. */
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
VPATH += $(SRC_PATH)/tests/tcg/m68k
|
VPATH += $(SRC_PATH)/tests/tcg/m68k
|
||||||
TESTS += trap
|
TESTS += trap denormal
|
||||||
|
|
||||||
# On m68k Linux supports 4k and 8k pages (but 8k is currently broken)
|
# On m68k Linux supports 4k and 8k pages (but 8k is currently broken)
|
||||||
EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192
|
EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192
|
||||||
|
53
tests/tcg/m68k/denormal.c
Normal file
53
tests/tcg/m68k/denormal.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Test m68k extended double denormals.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define TEST(X, Y) { X, Y, X * Y }
|
||||||
|
|
||||||
|
static volatile long double test[][3] = {
|
||||||
|
TEST(0x1p+16383l, 0x1p-16446l),
|
||||||
|
TEST(0x1.1p-8223l, 0x1.1p-8224l),
|
||||||
|
TEST(1.0l, 0x1p-16383l),
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef TEST
|
||||||
|
|
||||||
|
static void dump_ld(const char *label, long double ld)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
long double d;
|
||||||
|
struct {
|
||||||
|
uint32_t exp:16;
|
||||||
|
uint32_t space:16;
|
||||||
|
uint32_t h;
|
||||||
|
uint32_t l;
|
||||||
|
};
|
||||||
|
} u;
|
||||||
|
|
||||||
|
u.d = ld;
|
||||||
|
printf("%12s: % -27La 0x%04x 0x%08x 0x%08x\n", label, u.d, u.exp, u.h, u.l);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int i, n = sizeof(test) / sizeof(test[0]), err = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
long double x = test[i][0];
|
||||||
|
long double y = test[i][1];
|
||||||
|
long double build_mul = test[i][2];
|
||||||
|
long double runtime_mul = x * y;
|
||||||
|
|
||||||
|
if (runtime_mul != build_mul) {
|
||||||
|
dump_ld("x", x);
|
||||||
|
dump_ld("y", y);
|
||||||
|
dump_ld("build_mul", build_mul);
|
||||||
|
dump_ld("runtime_mul", runtime_mul);
|
||||||
|
err = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user