Skip to content

Commit 82d6d38

Browse files
committed
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 <[email protected]> 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.
1 parent 6c152c9 commit 82d6d38

File tree

2 files changed

+75
-56
lines changed

2 files changed

+75
-56
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* PR libgcc/114755 */
2+
/* { dg-do run { target bitint } } */
3+
/* { dg-options "-std=c23" } */
4+
/* { dg-skip-if "" { ! run_expensive_tests } { "*" } { "-O0" "-O2" } } */
5+
/* { dg-skip-if "" { ! run_expensive_tests } { "-flto" } { "" } } */
6+
7+
#if __BITINT_MAXWIDTH__ >= 255
8+
_BitInt(65)
9+
foo (void)
10+
{
11+
_BitInt(255) a = 0x040404040404040404040404wb;
12+
_BitInt(65) b = -0xffffffffffffffffwb;
13+
_BitInt(65) r = a % b;
14+
return r;
15+
}
16+
#endif
17+
18+
int
19+
main ()
20+
{
21+
#if __BITINT_MAXWIDTH__ >= 255
22+
_BitInt(65) x = foo ();
23+
if (x != 0x0404040408080808wb)
24+
__builtin_abort ();
25+
#endif
26+
}

libgcc/libgcc2.c

Lines changed: 49 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,69 +1705,62 @@ __divmodbitint4 (UBILtype *q, SItype qprec,
17051705
USItype rn = ((USItype) rprec + W_TYPE_SIZE - 1) / W_TYPE_SIZE;
17061706
USItype up = auprec % W_TYPE_SIZE;
17071707
USItype vp = avprec % W_TYPE_SIZE;
1708-
if (__builtin_expect (un < vn, 0))
1708+
/* If vprec < 0 and the top limb of v is all ones and the second most
1709+
significant limb has most significant bit clear, then just decrease
1710+
vn/avprec/vp, because after negation otherwise v2 would have most
1711+
significant limb clear. */
1712+
if (vprec < 0
1713+
&& ((v[BITINT_END (0, vn - 1)] | (vp ? ((UWtype) -1 << vp) : 0))
1714+
== (UWtype) -1)
1715+
&& vn > 1
1716+
&& (Wtype) v[BITINT_END (1, vn - 2)] >= 0)
17091717
{
1710-
/* If abs(v) > abs(u), then q is 0 and r is u.
1711-
Unfortunately un < vn doesn't always mean abs(v) > abs(u).
1712-
If uprec > 0 and vprec < 0 and vn == un + 1, if the
1713-
top limb of v is all ones and the second most significant
1714-
limb has most significant bit clear, then just decrease
1715-
vn/avprec/vp and continue, after negation both numbers
1716-
will have the same number of limbs. */
1717-
if (un + 1 == vn
1718-
&& uprec >= 0
1719-
&& vprec < 0
1720-
&& ((v[BITINT_END (0, vn - 1)] | (vp ? ((UWtype) -1 << vp) : 0))
1721-
== (UWtype) -1)
1722-
&& (Wtype) v[BITINT_END (1, vn - 2)] >= 0)
1723-
{
1724-
vp = 0;
1725-
--vn;
1718+
vp = 0;
1719+
--vn;
17261720
#if __LIBGCC_BITINT_ORDER__ == __ORDER_BIG_ENDIAN__
1727-
++v;
1721+
++v;
17281722
#endif
1723+
}
1724+
if (__builtin_expect (un < vn, 0))
1725+
{
1726+
/* q is 0 and r is u. */
1727+
if (q)
1728+
__builtin_memset (q, 0, qn * sizeof (UWtype));
1729+
if (r == NULL)
1730+
return;
1731+
#if __LIBGCC_BITINT_ORDER__ == __ORDER_BIG_ENDIAN__
1732+
r += rn - 1;
1733+
u += un - 1;
1734+
#endif
1735+
if (up)
1736+
--un;
1737+
if (rn < un)
1738+
un = rn;
1739+
for (rn -= un; un; --un)
1740+
{
1741+
*r = *u;
1742+
r += BITINT_INC;
1743+
u += BITINT_INC;
17291744
}
1730-
else
1745+
if (!rn)
1746+
return;
1747+
if (up)
17311748
{
1732-
/* q is 0 and r is u. */
1733-
if (q)
1734-
__builtin_memset (q, 0, qn * sizeof (UWtype));
1735-
if (r == NULL)
1736-
return;
1737-
#if __LIBGCC_BITINT_ORDER__ == __ORDER_BIG_ENDIAN__
1738-
r += rn - 1;
1739-
u += un - 1;
1740-
#endif
1741-
if (up)
1742-
--un;
1743-
if (rn < un)
1744-
un = rn;
1745-
for (rn -= un; un; --un)
1746-
{
1747-
*r = *u;
1748-
r += BITINT_INC;
1749-
u += BITINT_INC;
1750-
}
1751-
if (!rn)
1749+
if (uprec > 0)
1750+
*r = *u & (((UWtype) 1 << up) - 1);
1751+
else
1752+
*r = *u | ((UWtype) -1 << up);
1753+
r += BITINT_INC;
1754+
if (!--rn)
17521755
return;
1753-
if (up)
1754-
{
1755-
if (uprec > 0)
1756-
*r = *u & (((UWtype) 1 << up) - 1);
1757-
else
1758-
*r = *u | ((UWtype) -1 << up);
1759-
r += BITINT_INC;
1760-
if (!--rn)
1761-
return;
1762-
}
1763-
UWtype c = uprec < 0 ? (UWtype) -1 : (UWtype) 0;
1764-
for (; rn; --rn)
1765-
{
1766-
*r = c;
1767-
r += BITINT_INC;
1768-
}
1769-
return;
17701756
}
1757+
UWtype c = uprec < 0 ? (UWtype) -1 : (UWtype) 0;
1758+
for (; rn; --rn)
1759+
{
1760+
*r = c;
1761+
r += BITINT_INC;
1762+
}
1763+
return;
17711764
}
17721765
USItype qn2 = un - vn + 1;
17731766
if (qn >= qn2)

0 commit comments

Comments
 (0)