diff options
author | Richard Henderson <richard.henderson@linaro.org> | 2023-08-20 17:28:33 -0700 |
---|---|---|
committer | Richard Henderson <richard.henderson@linaro.org> | 2023-09-16 14:57:16 +0000 |
commit | 722460652b3aee89dc19df61f1f33df53a9b97c9 (patch) | |
tree | 84cca92504817a5b1ccb466fa918e8cebb262090 | |
parent | 00f9ef8f3dd6940001311a6230985243c3ebb996 (diff) | |
download | qemu-722460652b3aee89dc19df61f1f33df53a9b97c9.zip qemu-722460652b3aee89dc19df61f1f33df53a9b97c9.tar.gz qemu-722460652b3aee89dc19df61f1f33df53a9b97c9.tar.bz2 |
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>
-rw-r--r-- | fpu/softfloat-parts.c.inc | 7 | ||||
-rw-r--r-- | fpu/softfloat.c | 9 | ||||
-rw-r--r-- | tests/tcg/m68k/Makefile.target | 2 | ||||
-rw-r--r-- | tests/tcg/m68k/denormal.c | 53 |
4 files changed, 66 insertions, 5 deletions
diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 527e15e..a44649f 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -118,7 +118,8 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status, } else { int shift = frac_normalize(p); 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) { 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); } - frac_shrjam(p, 1 - exp); + frac_shrjam(p, !fmt->m68k_denormal - exp); if (p->frac_lo & round_mask) { /* 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; } - exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) != 0; + exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !fmt->m68k_denormal; frac_shr(p, frac_shift); if (is_tiny && (flags & float_flag_inexact)) { diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 2a33967..027a8e5 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -517,6 +517,7 @@ typedef struct { * round_mask: bits below lsb which must be rounded * The following optional modifiers are available: * arm_althp: handle ARM Alternative Half Precision + * m68k_denormal: explicit integer bit for extended precision may be 1 */ typedef struct { int exp_size; @@ -526,6 +527,7 @@ typedef struct { int frac_size; int frac_shift; bool arm_althp; + bool m68k_denormal; uint64_t round_mask; } FloatFmt; @@ -576,7 +578,12 @@ static const FloatFmt float128_params = { static const FloatFmt floatx80_params[3] = { [floatx80_precision_s] = { FLOATX80_PARAMS(23) }, [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. */ diff --git a/tests/tcg/m68k/Makefile.target b/tests/tcg/m68k/Makefile.target index 1163c7e..6ff214e 100644 --- a/tests/tcg/m68k/Makefile.target +++ b/tests/tcg/m68k/Makefile.target @@ -4,7 +4,7 @@ # VPATH += $(SRC_PATH)/tests/tcg/m68k -TESTS += trap +TESTS += trap denormal # On m68k Linux supports 4k and 8k pages (but 8k is currently broken) EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192 diff --git a/tests/tcg/m68k/denormal.c b/tests/tcg/m68k/denormal.c new file mode 100644 index 0000000..20bd8c7 --- /dev/null +++ b/tests/tcg/m68k/denormal.c @@ -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; +} |