diff options
Diffstat (limited to 'gitlab/issues/target_m68k/host_missing/accel_TCG/2290.toml')
| -rw-r--r-- | gitlab/issues/target_m68k/host_missing/accel_TCG/2290.toml | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/gitlab/issues/target_m68k/host_missing/accel_TCG/2290.toml b/gitlab/issues/target_m68k/host_missing/accel_TCG/2290.toml new file mode 100644 index 000000000..3e0c124cf --- /dev/null +++ b/gitlab/issues/target_m68k/host_missing/accel_TCG/2290.toml @@ -0,0 +1,151 @@ +id = 2290 +title = "Wrong multiplication result of 'long double' on m68k" +state = "closed" +created_at = "2024-04-18T11:31:12.121Z" +closed_at = "2024-04-18T22:26:41.012Z" +labels = ["Softfloat", "Stable::to backport", "accel: TCG", "kind::Bug", "target: m68k"] +url = "https://gitlab.com/qemu-project/qemu/-/issues/2290" +host-os = "Ubuntu 22.04" +host-arch = "x86_64" +qemu-version = "8.1.2" +guest-os = "Debian 12" +guest-arch = "m68k" +description = """In both x86 and m68k, 'long double' is an 80-bit format consisting of + - 1 bit sign, 15 bits exponent, + - 1 explicit 1 bit, 63 fraction bits. + +According to <https://en.wikipedia.org/wiki/Extended_precision> and +<https://www.nxp.com/docs/en/reference-manual/M68000PRM.pdf> table 1-6 (page 1-23), with two differences: + - In m68k, there are 16 zero bits as filler after the sign/exponent + word, so that the total size is 96 bits. + - In x86, the minimum exponent of normalized numbers is 1; + in m68k, the minimum exponent of normalized numbers is 0. + +The latter difference is reflected in the values of LDBL_MIN_EXP and +LDBL_MIN in gcc: + +In x86: +``` +$ echo '#include <float.h>' | gcc -E -dM - | grep __LDBL_MIN_EXP_ +#define LDBL_MIN_EXP __LDBL_MIN_EXP__ +#define __LDBL_MIN_EXP__ (-16381) +$ echo '#include <float.h>' | gcc -E -dM - | grep __LDBL_MIN__ +#define __LDBL_MIN__ 3.36210314311209350626267781732175260e-4932L +#define LDBL_MIN __LDBL_MIN__ +``` +In m68k (I use Debian 12/Linux): +``` +$ echo '#include <float.h>' | gcc -E -dM - | grep __LDBL_MIN_EXP_ +#define LDBL_MIN_EXP __LDBL_MIN_EXP__ +#define __LDBL_MIN_EXP__ (-16382) +$ echo '#include <float.h>' | gcc -E -dM - | grep __LDBL_MIN__ +#define __LDBL_MIN__ 1.68105157155604675313e-4932L +#define LDBL_MIN __LDBL_MIN__ +```""" +reproduce = """Take this program, foo.c: +``` +/* Show extended-precision https://en.wikipedia.org/wiki/Extended_precision + multiplication bug in QEMU. */ + +#include <stdio.h> + +static void +show (const long double *p) +{ +#ifdef __m68k__ + printf("<S,E: 0x%08X M: 0x%08X%08X>", + ((const unsigned int *) p)[0], + ((const unsigned int *) p)[1], + ((const unsigned int *) p)[2]); +#else /* x86 */ + printf("<S,E: 0x%04X M: 0x%08X%08X>", + ((const unsigned short *) p)[4], + ((const unsigned int *) p)[1], + ((const unsigned int *) p)[0]); +#endif + printf (" = %La = %Lg", *p, *p); +} + +static void +show_mult (long double a, long double b) +{ + printf ("Factors: "); + show (&a); + printf ("\\n and: "); + show (&b); + long double c = a * b; + printf ("\\nProduct: "); + show (&c); + printf ("\\n\\n"); +} + +/* Return 2^n. */ +static long double +pow2l (int n) +{ + int k = n; + volatile long double x = 1; + volatile long double y = 2; + /* Invariant: 2^n == x * y^k. */ + if (k < 0) + { + y = 0.5L; + k = - k; + } + while (k > 0) + { + if (k != 2 * (k / 2)) + { + x = x * y; + k = k - 1; + } + if (k == 0) + break; + y = y * y; + k = k / 2; + } + /* Now k == 0, hence x == 2^n. */ + return x; +} + +int main () +{ + show_mult (pow2l (-16382), 0.5L); + show_mult (pow2l (-16381), 0.25L); + return 0; +} +``` +Its output on x86: +``` +$ ./a.out +Factors: <S,E: 0x0001 M: 0x8000000000000000> = 0x8p-16385 = 3.3621e-4932 + and: <S,E: 0x3FFE M: 0x8000000000000000> = 0x8p-4 = 0.5 +Product: <S,E: 0x0000 M: 0x4000000000000000> = 0x4p-16385 = 1.68105e-4932 + +Factors: <S,E: 0x0002 M: 0x8000000000000000> = 0x8p-16384 = 6.72421e-4932 + and: <S,E: 0x3FFD M: 0x8000000000000000> = 0x8p-5 = 0.25 +Product: <S,E: 0x0000 M: 0x4000000000000000> = 0x4p-16385 = 1.68105e-4932 +``` +Its output on m68k: +``` +$ ./a.out +Factors: <S,E: 0x00010000 M: 0x8000000000000000> = 0x8p-16385 = 3.3621e-4932 + and: <S,E: 0x3FFE0000 M: 0x8000000000000000> = 0x8p-4 = 0.5 +Product: <S,E: 0x00000000 M: 0x4000000000000000> = 0x4p-16386 = 8.40526e-4933 + +Factors: <S,E: 0x00020000 M: 0x8000000000000000> = 0x8p-16384 = 6.72421e-4932 + and: <S,E: 0x3FFD0000 M: 0x8000000000000000> = 0x8p-5 = 0.25 +Product: <S,E: 0x00000000 M: 0x4000000000000000> = 0x4p-16386 = 8.40526e-4933 +``` +The product, computed by QEMU, is incorrect. It is only half as large as the +correct value. The expected output should be: +``` +Factors: <S,E: 0x00010000 M: 0x8000000000000000> = 0x8p-16385 = 3.3621e-4932 + and: <S,E: 0x3FFE0000 M: 0x8000000000000000> = 0x8p-4 = 0.5 +Product: <S,E: 0x00000000 M: 0x8000000000000000> = 0x8p-16386 = 1.68105e-4932 + +Factors: <S,E: 0x00020000 M: 0x8000000000000000> = 0x8p-16384 = 6.72421e-4932 + and: <S,E: 0x3FFD0000 M: 0x8000000000000000> = 0x8p-5 = 0.25 +Product: <S,E: 0x00000000 M: 0x8000000000000000> = 0x8p-16386 = 1.68105e-4932 +```""" +additional = """In QEMU's source code, I would guess that this multiplication is performed by the `floatx80_mul` function.""" |