aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2024-04-18 09:49:02 +0200
committerJakub Jelinek <jakub@redhat.com>2024-04-18 09:49:02 +0200
commit82d6d385f9708fb6d5e2a2bacd003155cfc41c08 (patch)
treefb514415fa26b47c9b0d3b5d1958ba21f02fb5b3 /gcc
parent6c152c9db3b5b9d43e12846fb7a44977c0b65fc2 (diff)
downloadgcc-82d6d385f9708fb6d5e2a2bacd003155cfc41c08.zip
gcc-82d6d385f9708fb6d5e2a2bacd003155cfc41c08.tar.gz
gcc-82d6d385f9708fb6d5e2a2bacd003155cfc41c08.tar.bz2
libgcc: Fix up __divmodbitint4 [PR114755]
The following testcase aborts on aarch64-linux but does not on x86_64-linux. In both cases there is UB in the __divmodbitint4 implemenetation. When the divisor is negative with most significant limb (even when partial) all ones, has at least 2 limbs and the second most significant limb has the most significant bit clear, when this number is negated, it will have 0 in the most significant limb. Already in the PR114397 r14-9592 fix I was dealing with such divisors, but thought the problem is only if because of that un < vn doesn't imply the quotient is 0 and remainder u. But as this testcase shows, the problem is with such divisors always. What happens is that we use __builtin_clz* on the most significant limb, and assume it will not be 0 because that is UB for the builtins. Normally the most significant limb of the divisor shouldn't be 0, as guaranteed by the bitint_reduce_prec e.g. for the positive numbers, unless the divisor is just 0 (but for vn == 1 we have special cases). The following patch moves the handling of this corner case a few lines earlier before the un < vn check, because adjusting the vn later is harder. 2024-04-18 Jakub Jelinek <jakub@redhat.com> PR libgcc/114755 * libgcc2.c (__divmodbitint4): Perform the decrement on negative v with most significant limb all ones and the second least significant limb with most significant bit clear always, regardless of un < vn. * gcc.dg/torture/bitint-69.c: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/testsuite/gcc.dg/torture/bitint-69.c26
1 files changed, 26 insertions, 0 deletions
diff --git a/gcc/testsuite/gcc.dg/torture/bitint-69.c b/gcc/testsuite/gcc.dg/torture/bitint-69.c
new file mode 100644
index 0000000..5f89357
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/bitint-69.c
@@ -0,0 +1,26 @@
+/* PR libgcc/114755 */
+/* { dg-do run { target bitint } } */
+/* { dg-options "-std=c23" } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
+/* { dg-skip-if "" { ! run_expensive_tests } { "-flto" } { "" } } */
+
+#if __BITINT_MAXWIDTH__ >= 255
+_BitInt(65)
+foo (void)
+{
+ _BitInt(255) a = 0x040404040404040404040404wb;
+ _BitInt(65) b = -0xffffffffffffffffwb;
+ _BitInt(65) r = a % b;
+ return r;
+}
+#endif
+
+int
+main ()
+{
+#if __BITINT_MAXWIDTH__ >= 255
+ _BitInt(65) x = foo ();
+ if (x != 0x0404040408080808wb)
+ __builtin_abort ();
+#endif
+}