https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127490
Bug ID: 127490
Summary: Documented __builtin_stdc_rotate_left/right formula is
incorrect for non-power-of-two _BitInt widths
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: other
Assignee: unassigned at gcc dot gnu.org
Reporter: bic60176 at gmail dot com
Target Milestone: ---
The documented equivalence for __builtin_stdc_rotate_left and
__builtin_stdc_rotate_right is incorrect for unsigned integer types whose
precision is not a power-of-two divisor of the width of type2.
The current documentation describes __builtin_stdc_rotate_left(arg1, arg2)
as equivalent to an expression containing:
arg1 >> ((-arg2) % prec)
where the negation is performed in type2.
For an unsigned type2, (-arg2) is computed using unsigned wraparound before
the modulo operation. In general:
((-arg2 in type2) % prec)
is not equivalent to:
((prec - (arg2 % prec)) % prec)
unless the modulus of type2 is itself divisible by prec.
This becomes observable with an unsigned _BitInt type having a non-power-of-two
precision.
Test case:
int main(void)
{
unsigned _BitInt(3) x = 1;
unsigned _BitInt(3) actual =
__builtin_stdc_rotate_left(x, 1);
unsigned _BitInt(3) documented =
(unsigned _BitInt(3))
((x << (1 % 3)) |
(x >> ((-(unsigned)1) % 3)));
return actual == 2 && documented == 3 ? 0 : 1;
}
Command:
gcc -std=c23 -O0 test.c -o test
./test
echo $?
Observed behavior:
The builtin correctly produces 2. However, the expression derived from the
documented equivalence produces 3:
1 % 3 == 1
(-(unsigned)1) % 3 == 0
Therefore, the documented expression effectively computes:
(1 << 1) | (1 >> 0) == 3
Expected documentation:
The documentation should express the complementary shift count without relying
on unsigned negation before taking the remainder. Conceptually, the count
should be equivalent to:
(prec - (arg2 % prec)) % prec
or another expression that correctly handles zero and non-power-of-two
precisions.
This appears to be a documentation defect only. The implementation of
__builtin_stdc_rotate_left produces the expected result.