diff options
author | Jakub Jelinek <jakub@redhat.com> | 2024-04-18 09:49:02 +0200 |
---|---|---|
committer | Jakub Jelinek <jakub@redhat.com> | 2024-04-18 09:49:02 +0200 |
commit | 82d6d385f9708fb6d5e2a2bacd003155cfc41c08 (patch) | |
tree | fb514415fa26b47c9b0d3b5d1958ba21f02fb5b3 /gcc | |
parent | 6c152c9db3b5b9d43e12846fb7a44977c0b65fc2 (diff) | |
download | gcc-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.c | 26 |
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 +} |