https://gcc.gnu.org/g:69093fd8aa682a1b906e80b3c5f10956e692b7c4
commit r15-2752-g69093fd8aa682a1b906e80b3c5f10956e692b7c4 Author: Jakub Jelinek <ja...@redhat.com> Date: Tue Aug 6 11:09:33 2024 +0200 wide-int: Fix up mul_internal overflow checking [PR116224] The following testcase is miscompiled, because wi::mul for (_BitInt(65))-15 times (_BitInt(65))-15 computes the right value (_BitInt(65))225, but sets *overflow to wi::OVF_UNKNOWN as that it overflowed when it didn't. Even signed operands are unpacked as unsigned but because they are implicitly sign-extended from the represented value (the operands obviously have len==1), we get 0xfffffff1, 0xffffffff, 0x1, 0x0 in both u and v (0x1 because that is exactly 65 bits). We then multiply these. Next step is because both the high and overflow handling expects the high half to start at a limb boundary the bits of the result starting with bit 65 are shifted up by 63 such that the bits relevant for high/need_overflow start at the half of the 4th half wide int limb. Because both operands are negative that part is then adjusted. The reason mul_internal says there is overflow is because of the unspecified garbage in the most significant bits of the result which the adjusting doesn't clean up. 65 bit multiplication needs 65 bits of result and 65 bits of the high part, can't produce more, so the following patch fixes it by checking for the overflow only in those first 65 bits of the high part, not anything beyond that. If it was a highpart multiply, we'd have ignored that as well (canonicalized). 2024-08-06 Jakub Jelinek <ja...@redhat.com> PR tree-optimization/116224 * wide-int.cc (wi::mul_internal): If prec isn't multiple of HOST_BITS_PER_WIDE_INT, for need_overflow checking only look at the least significant prec bits starting with r[half_blocks_needed]. * gcc.dg/torture/bitint-72.c: New test. Diff: --- gcc/testsuite/gcc.dg/torture/bitint-72.c | 28 ++++++++++++++++++++++++++++ gcc/wide-int.cc | 19 ++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/gcc/testsuite/gcc.dg/torture/bitint-72.c b/gcc/testsuite/gcc.dg/torture/bitint-72.c new file mode 100644 index 000000000000..101018b00eeb --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/bitint-72.c @@ -0,0 +1,28 @@ +/* PR tree-optimization/116224 */ +/* { 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__ >= 65 +#define N 65 +#else +#define N 63 +#endif + +signed char g; + +int +foo (signed char c, int i, _BitInt(N) b) +{ + __builtin_memmove (&g, &b, 1); + return b / i / c; +} + +int +main () +{ + int x = foo (-15, -15, 900); + if (x != 4) + __builtin_abort (); +} diff --git a/gcc/wide-int.cc b/gcc/wide-int.cc index 9c6eba807aad..5bd16a595643 100644 --- a/gcc/wide-int.cc +++ b/gcc/wide-int.cc @@ -1574,7 +1574,24 @@ wi::mul_internal (HOST_WIDE_INT *val, const HOST_WIDE_INT *op1val, top &= mask; } - for (i = half_blocks_needed; i < half_blocks_needed * 2; i++) + unsigned int end = half_blocks_needed * 2; + shift = prec % HOST_BITS_PER_WIDE_INT; + if (shift) + { + /* For overflow checking only look at the first prec bits + starting with r[half_blocks_needed]. */ + if (shift <= HOST_BITS_PER_HALF_WIDE_INT) + --end; + shift %= HOST_BITS_PER_HALF_WIDE_INT; + if (shift) + { + if (top) + r[end - 1] |= ((~(unsigned HOST_HALF_WIDE_INT) 0) << shift); + else + r[end - 1] &= (((unsigned HOST_HALF_WIDE_INT) 1) << shift) - 1; + } + } + for (i = half_blocks_needed; i < end; i++) if (((HOST_WIDE_INT)(r[i] & mask)) != top) /* FIXME: Signed overflow type is not implemented yet. */ *overflow = (sgn == UNSIGNED) ? wi::OVF_OVERFLOW : wi::OVF_UNKNOWN;