summary refs log tree commit diff stats
path: root/gitlab/issues/target_m68k/host_missing/accel_TCG/2290.toml
diff options
context:
space:
mode:
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.toml151
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."""