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.

Reply via email to